查找一个表中的字符串是否是另一个列表中字符串的子字符串

时间:2012-12-05 10:49:13

标签: mysql loops select substring

我正在分析一个类似拼字游戏的文字游戏,以查明棋盘上放置的一串字母是否会使得无法放置更多的字母,即“锁定”游戏。让我试着通过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。

作为旁注 - 我真的怀疑存在这样的锁定矩阵,但这就是我正在检查的内容。

2 个答案:

答案 0 :(得分:1)

现有查询存在的问题是扫描子字符串效率极低。特别是,无法使用索引 - 因此需要对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来查找此类模式,索引不能超越最初的已知字符;如果这符合您的要求,可以对数据结构进行进一步修改。

填充并维护suffixes

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

可以创建几个command来帮助填充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;;
    
  3. 还可以定义stored procedures以根据suffixes表的更改自动维护dic_word表:

    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;;
    

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

    CALL FillAllSuffixes;
    

答案 1 :(得分:0)

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

它将循环遍历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 ;
相关问题