【问题标题】:Finding what words a set of letters can create?找出一组字母可以创建哪些单词?
【发布时间】:2013-02-17 01:14:05
【问题描述】:

我正在尝试编写一些 SQL 来接受一组字母并返回它可以组成的所有可能的单词。我的第一个想法是创建一个基本的三表数据库,如下所示:

Words -- contains 200k words in real life
------
1 | act
2 | cat

Letters -- contains the whole alphabet in real life
--------
1  | a
3  | c
20 | t

WordLetters --First column is the WordId and the second column is the LetterId
------------
1  | 1
1  | 3
1  | 20
2  | 3
2  | 1
2  | 20

但是我有点纠结于如何编写一个查询,该查询返回在 WordLetters 中为每个传入的字母都有一个条目的单词。它还需要考虑具有两个相同字母的单词。我从这个查询开始,但它显然不起作用:

SELECT DISTINCT w.Word 
FROM Words w
INNER JOIN WordLetters wl
ON wl.LetterId = 20 AND wl.LetterId = 3 AND wl.LetterId = 1

如何编写查询以仅返回包含所有传入字母并考虑重复字母的单词?


其他信息:

我的 Word 表包含近 200,000 个单词,这就是我尝试在数据库端而不是在代码中执行此操作的原因。如果有人关心,我正在使用enable1 word list

【问题讨论】:

  • 有趣的问题,但我想尽可能多地在代码中做这件事。
  • 我考虑过,但在内存中处理 200k 字似乎相当耗费资源。你能指出我有什么资源可以通过有效的方式在代码中处理这种数据吗?
  • 我原以为您可以检索这 200,000 个单词的子集以在代码中使用?您是否考虑过调整字谜算法以适应此目的?
  • 200K 单词乘以平均每个单词 7-8 个字符(对于某些语言可能是最坏的情况)将产生约 1.5-3Mb 的纯数据(取决于您是否使用 Unicode 等)。当然不是大量的数据可供使用。

标签: sql sql-server sql-server-2008


【解决方案1】:

暂时忽略问题的 SQL 部分,我使用的算法相当简单:首先获取字典中的每个单词,然后生成一个版本,其中的字母按排序顺序,以及指向该单词原始版本的指针。

这将给出一个包含以下条目的表格:

sorted_text word_id
act         123    /* we'll assume `act` was word number 123 in the original list */
act         321    /* we'll assume 'cat' was word number 321 in the original list */

然后,当我们收到一个输入(比如“tac”)时,我们对其字母进行排序,在与原始单词表连接的排序字母表中查找它,这给了我们一个单词列表,可以从该输入创建。

如果 这样做,我会在 SQL 数据库中拥有相应的表,但可能会使用其他东西将单词列表预处理为排序形式。同样,我可能会将用户输入的字母排序留给我用来创建前端的任何内容,这样 SQL 就可以做它擅长的事情:关系数据库管理。

【讨论】:

  • 我喜欢它。非常聪明的解决方案。
【解决方案2】:

如果您使用您提供的解决方案,您需要在 WordLetters 表中添加一个订单列。否则,无法保证您检索的行与插入它们的顺序相同。

但是,我认为我有更好的解决方案。根据您的问题,您似乎希望找到具有相同组成字母的所有单词,而与出现的顺序或次数无关。这意味着您的可能性有限。如果将字母表中的每个字母转换为不同的 2 次幂,则可以为每个字母组合创建一个唯一值(也称为位掩码)。然后,您可以简单地将单词中每个字母的值相加。这将使匹配单词变得微不足道,因为具有相同字母的所有单词都将映射到相同的值。这是一个例子:

WITH letters
     AS (SELECT Cast('a' AS VARCHAR) AS Letter,
                1                    AS LetterValue,
                1                    AS LetterNumber
         UNION ALL
         SELECT Cast(Char(97 + LetterNumber) AS VARCHAR),
                Power(2, LetterNumber),
                LetterNumber + 1
         FROM   letters
         WHERE  LetterNumber < 26),
     words
     AS (SELECT 1 AS wordid, 'act' AS word
         UNION ALL SELECT 2, 'cat'
         UNION ALL SELECT 3, 'tom'
         UNION ALL SELECT 4, 'moot'
         UNION ALL SELECT 5, 'mote')
SELECT wordid,
       word,
       Sum(distinct LetterValue) as WordValue
FROM   letters
       JOIN words
         ON word LIKE '%' + letter + '%'
GROUP  BY wordid, word

如果您运行此查询,您将看到,“act”和“cat”具有相同的 WordValue,“tom”和“moot”也是如此,尽管字符数不同。

是什么让这比您的解决方案更好?您不必构建很多非单词来清除它们。这将大大节省执行任务所需的存储和处理。

【讨论】:

    【解决方案3】:

    在 SQL 中有一个解决方案。它涉及使用一种技巧来计算每个字母在单词中出现的次数。以下表达式计算“a”出现的次数:

    select len(word) - len(replace(word, 'a', ''))
    

    这个想法是计算单词中所有字母的总和,看看是否与总长度匹配:

    select w.word, (LEN(w.word) - SUM(LettersInWord))
    from 
    (
      select w.word, (LEN(w.word) - LEN(replace(w.word, wl.letter))) as LettersInWord
      from word w 
      cross join wordletters wl
    ) wls
    having (LEN(w.word) = SUM(LettersInWord))
    

    这个特殊的解决方案允许一个字母多次出现。我不确定在原始问题中是否需要这样做。如果我们想要达到一定数量的出现,那么我们可能会执行以下操作:

    select w.word, (LEN(w.word) - SUM(LettersInWord))
    from 
    (
       select w.word,
         (case when (LEN(w.word) - LEN(replace(w.word, wl.letter))) <= maxcount 
             then (LEN(w.word) - LEN(replace(w.word, wl.letter))) 
             else maxcount end) as LettersInWord
       from word w 
       cross join
       (
          select letter, count(*) as maxcount
          from wordletters wl
          group by letter
       ) wl
    ) wls
    having (LEN(w.word) = SUM(LettersInWord))
    

    如果您希望与字母完全匹配,则 case 语句应使用 " = maxcount" 而不是 " &lt;= maxcount"

    根据我的经验,我实际上已经看到小型交叉联接的性能不错。这实际上可能在服务器端工作。在服务器上进行这项工作有两大优势。首先,它利用了盒子上的并行性。其次,需要通过网络传输的数据要少得多。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-02-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多