【问题标题】:Finding if a string in one table is substring of strings in another list查找一个表中的字符串是否是另一个列表中字符串的子字符串
【发布时间】:2012-11-23 04:12:51
【问题描述】:

我正在分析一个类似拼字游戏的文字游戏,以找出放置在棋盘上的一个字母块是否会导致无法放置更多字母,即“锁定”游戏。让我尝试以 2x2 块为例进行解释:

我已经建立了一个有效的 2x2 块列表(大约 5000 个块)。该列表如下所示:

matrix_2x2

AA,AA
AA,AB
AA,AD
AA,AE
etc...

在棋盘上“AA,AE”看起来像这样(真正的棋盘是 15x15):

[ ][ ][ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ][ ][ ]
[ ][ ][ ][A][A][ ][ ][ ]
[ ][ ][ ][A][E][ ][ ][ ]
[ ][ ][ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ][ ][ ]

我也有完整的有效单词列表。看起来是这样的

dic_word

AA
AAH
AAHED
AAHING
AAHS
AAL
etc...

我在 MySQL 中有两个列表。

我知道我可以在代码中通过迭代矩阵列表中的每个条目并进行 SELECT 查询来做到这一点。每个矩阵行都是这样的:

SELECT COUNT(word) > 0 FROM dic_word d WHERE (INSTR(word, "AA") OR INSTR(word, "AE") OR INSTR(word, "AA") OR INSTR(word, "AE")) AND (word <> "AA" AND word <> "AB" AND word <> "AA" AND word <> "AB")

我只是想知道这是否可以完全在 MySQL 中完成。


更新

上面的 sql 查询确实有效,也不能很好地解释我所追求的。让我试着澄清一下我所追求的:

假设QXQQXX 都是英文的合法词,那么 QX,QX 将是我矩阵列表中的一个条目。

由于QXQQXX 是任何英文单词的子字符串,因此放置在棋盘上的QX、QX 将“锁定”游戏(即无法放置额外的字母)。

我关注这些锁定矩阵,并从查看所有有效的 2x2 矩阵开始。正如我们所说,我正在构建所有有效 3x3 块的列表 - 到目前为止已发现超过 200.000 个。

附带说明 - 我真的怀疑这种锁矩阵是否存在,但这就是我要检查的内容。

【问题讨论】:

  • 也许可以在 MySql 中做一些事情,但是鉴于您的示例数据,尚不清楚您所追求的结果...您能提供一些示例吗?
  • @fthiella 请检查我上面的更新,希望它能更好地解释我所追求的
  • 哦。在看到您的编辑之前,我发布了我的答案。现在我不确定您是否只是想测试是否可以为给定的“矩阵”找到字符串,或者正在寻找所有(在?)有效的“矩阵”。无论如何,我怀疑我提出的结构可以为有效构建此类矩阵奠定良好的基础。
  • @eggyal 感谢您的努力看起来很有趣 - 请参阅我的“答案”,它应该可以更好地解释问题。

标签: mysql loops select substring


【解决方案1】:

到目前为止,这是我的解决方案。

它将遍历所有 2x2 字母(矩阵)的块并计算可以放置多少个单词。然后将结果插入到表 lock2 中。如果一个矩阵的计数为 0,那么我们就有一个锁定的游戏情况。

它正在工作,但速度非常慢。 2x2 列表的性能是可以接受的,但 3x3 列表在我的有生之年不会结束 - 而且我只有 41 岁并且拥有一台相当快速的计算机。

非常欢迎提出提高性能的建议。

DELIMITER $$

DROP PROCEDURE IF EXISTS `wsolver`.`analyse2_2` $$
CREATE PROCEDURE analyse2_2()
BEGIN
  DECLARE done INT DEFAULT FALSE;
  DECLARE a INT;
  DECLARE b VARCHAR(45);

  DECLARE cur1 CURSOR FOR SELECT Matrix FROM matrix2x2;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

  OPEN cur1;

  read_loop: LOOP
    FETCH cur1 INTO a;

    IF done THEN
      LEAVE read_loop;
    END IF;


      SET @w1 = SUBSTRING(a, 1, 2);
      SET @w2 = SUBSTRING(a, 4, 2);
      SET @w3 = CONCAT(SUBSTRING(@w1, 1, 1), SUBSTRING(@w2, 1, 1));
      SET @w4 = CONCAT(SUBSTRING(@w1, 2, 1), SUBSTRING(@w2, 2, 1));


      SELECT COUNT(*) INTO @w5 FROM dan WHERE (instr(dic_word, @w1) OR instr(dic_word, @w2) OR instr(dic_word, @w3) OR instr(dic_word, @w4)) AND (dic_word NOT IN(@w1,@w2,@w3,@w4));

      INSERT INTO `lock2` (matrix, `count`) VALUES (a, @w5);


  END LOOP;

  CLOSE cur1;

END $$

DELIMITER ;

【讨论】:

    【解决方案2】:

    您现有查询的问题是扫描子字符串效率极低。特别是,不能使用索引,因此需要对 dic_word 表进行全面扫描,然后必须单独扫描其中的每个 word 以查找所需的子字符串。

    我会改为创建suffixes 的索引表:

    CREATE TABLE suffixes (
      suffix VARCHAR(15) NOT NULL,
      word   VARCHAR(15) NOT NULL,
      PRIMARY KEY (suffix, word),
      FOREIGN KEY (word) REFERENCES dic_word (word)
    );
    

    然后可以执行以下非常有效的查询:

    SELECT 1
    FROM   suffixes
    WHERE (
           suffix LIKE 'AA%'
        OR suffix LIKE 'AE%'
        OR suffix LIKE 'AA%'
        OR suffix LIKE 'AE%'
          ) AND word NOT IN ('AA','AE','AA','AE')
    LIMIT  1
    

    注意:

    • LIMIT 子句导致 MySQL 在找到单个结果后立即停止搜索;

    • 结果集要么包含一条记录以指示存在一个或多个可能的单词,要么不包含任何记录以指示没有可能的单词;

    • 此查询不考虑棋盘上剩余的空间——例如,如果包含子字符串的唯一单词太长以至于超出棋盘边缘,但是可以通过添加CHAR_LENGTH(word) 上的附加过滤器,如果需要,可以保存在另一个索引列中;

    • 这种优化并不容易扩展到更复杂的情况,例如存在间歇性空格和已知字母的地方——例如'A__DE____J__':虽然可以使用LIKE 来查找此类模式,但索引可以' t 帮助超出最初的已知字符;如果这是您的要求,可以对数据结构进行进一步修改。

     填充和维护suffixes

    对于本文的其余部分,我使用;; 作为我的语句分隔符——必须相应地配置自己的客户端:在MySQL command-line tool 中,这可以通过command DELIMITER ;; 来实现。

    可以创建几个stored procedures 来帮助填充suffixes 表:

    1. 添加给定单词的所有后缀:

      CREATE PROCEDURE FillSuffixes(IN p_word VARCHAR(15)) BEGIN
        DECLARE i TINYINT UNSIGNED DEFAULT 1;
        WHILE i <= CHAR_LENGTH(p_word) DO
          INSERT IGNORE INTO suffixes
            (suffix, word)
          VALUES
            (SUBSTRING(p_word, i), p_word)
          ;
          SET i := i + 1;
        END WHILE;
      END;;
      
    2. 添加dic_word 表中尚未在suffixes 表中的所有单词的所有后缀:

      CREATE PROCEDURE FillAllSuffixes() BEGIN
        DECLARE w VARCHAR(15);
        DECLARE done BOOLEAN DEFAULT FALSE;
      
        DECLARE cur CURSOR FOR
          SELECT word
          FROM   dic_word LEFT JOIN suffixes USING (word)
          WHERE  suffixes.word IS NULL
        ;
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
      
        OPEN cur;
      
        read_loop: LOOP
          FETCH cur INTO w;
          IF done THEN
            LEAVE read_loop;
          END IF;
          CALL FillSuffixes(w);
        END LOOP;
      
        CLOSE cur;
      END;;
      

    还可以定义triggers,以根据dic_word 表的更改自动维护suffixes 表:

    CREATE TRIGGER add_suffixes AFTER INSERT ON dic_word FOR EACH ROW
    CALL FillSuffixes(NEW.word);;
    
    CREATE TRIGGER upd_suffixes AFTER UPDATE ON dic_word FOR EACH ROW
    IF NEW.word <> OLD.word THEN
      DELETE FROM suffixes WHERE word = OLD.word;
      CALL FillSuffixes(NEW.word);
    END IF;;
    
    CREATE TRIGGER del_suffixes AFTER DELETE ON dic_word FOR EACH ROW
    DELETE FROM suffixes WHERE word = OLD.word;;
    

    最后,从现有记录中填充表(使用上面创建的过程 - 注意,可能需要一段时间才能运行):

    CALL FillAllSuffixes;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-05-11
      • 2020-08-30
      • 2022-07-01
      • 2013-05-12
      • 2011-02-07
      • 1970-01-01
      • 2015-03-21
      相关资源
      最近更新 更多