查找可以拼写给定字母组的单词集

时间:2011-03-16 15:47:55

标签: c# java c++ regex

我面临的问题是创建一个有效的算法,该算法返回固定单词列表中的单词列表,该单词列表可以通过使用一组特定字母(此集合作为输入)拼写,此集合没有上限(或至少没有用)。

更具体地说,该算法需要实时提供解决方案以实现持续增加的输入,尽管只要前几个输出元件相对较快地输出,输出就可以缓慢地流出。

**注意:输入的实际扩充仅以添加到现有集合的形式发生。当选择一个单词时,LetterStock将耗尽所有使用的字母,并且该算法将从头开始运行。

可能的字符集是26个标准罗马字母。每个字母的库存可以是0->无穷大(适用于所有密集目的),每个字母只能使用一次(例如a,b,l不会返回“ball”,尽管a,bb,d,lll会)

正在使用的单词列表是不变的,因此如果任何预先计算的散列解决方案对于加快运行时性能有用,那么这是一个选项(尽管我想不出这样做的好方法)。但是,单词列表很大(~40,000个元素)。

到目前为止,我设计的最佳算法是使用字母库存数组的副本迭代整个单词集,并逐字母检查每个单词,耗尽副本库存并查看是否有任何字母在下面0.如果是这样,我继续前进,如果没有,我到达单词的末尾,我将该单词添加到解决方案集中。在完成整套文字后,我从头开始再次寻找可以拼写的单词,即我已经将一个或多个字符添加到我的LetterStock中。

// Pass in the string and a copy of the LetterStock 26-entry array
bool canBeSpelled(string str, int *letterStock)
{
  for(int i=0; i<str.length; i++)
  {
    letterStock[str[i]-65]--; // deplete the letter stock for the given letter

    if(letterStock[str[i]-65] < 0)
      return false;

  }

  return true;
}

我的问题是:这个实施是否是最佳的,如果没有,哪个更好?

6 个答案:

答案 0 :(得分:1)

稍微开箱即用,但您是否考虑使用Sqlite之类的东西进行文字存储/查询?可以解决你所有的问题

答案 1 :(得分:1)

我认为你需要directed acyclic word graph(或DAWG)。我25年前首次在Scrabble程序中使用了这个结构(我们从WordPerfect for DOS中获取了字典)。如果您发现很难从您的单词列表中构建DAWG,请不要放弃 - 这是一个非常令人满意的成就。

答案 2 :(得分:0)

轻微优化,但如果您知道您的字母数量为5且字母中的字母数为6,则无需检查该字词。

答案 3 :(得分:0)

您需要多快的解决方案?即使在检查所有字符的最坏情况下,您的示例仅需要几毫秒(在我的基本测试中为2毫秒)来检查40,000个单词中的所有字母(总共338,000个字母)。所以在一秒钟内你可以用掉1.7亿个字符的股票。

如果符合我的需要,我通常更喜欢一种更简单的算法,而不是花时间/精力寻找可能最终只会稍微好一点的“最佳”算法。

答案 4 :(得分:0)

一些预处理可能有助于加快用户的速度。

在字母类中有一本字典:

public Dictionary<char, int> count = new Dictionary<char, int> { {'a', 0}, {'b', 0}, etc

在你让用户将你的单词列表处理成一个由单词键入的字典之前,用一个值设置一个字母类:

    List<string> strings = new List<string>{ "johnny", "happy", "people" };
    Dictionary< string, letters> processedWordList;

    void processList()
    {
        foreach (string s in strings)
        {
            //there should probably be a constructor in letters which does some of this 
            string lowered = s.ToLower();
            Letters letters;
            foreach (char c in lowered)
            {
                ++letters.count[c];
            }
            processedWordList.Add(s, letters);
        }
    }

然后你可以覆盖字母类中的&gt; =运算符,看看你是否拼写单词:

    public static bool operator >=(LetterCount c1, LetterCount c2)
    {
        foreach( KeyValuePair<char, int> letter in c1.count )
        {
            if (letter.Value < c2.count[letter.Key])
                return false;
        }
        return true;
    }

以类似的方式覆盖操作符以从输入中删除字母。然后您的主要功能如下:

        foreach( string s in strings )
        {
            if (input >= processedWordList[s])
            {
                input = input - processedWordList[s];
            }
        }

当然,所有未经测试。

答案 5 :(得分:0)

可用字符集可能有多大?如果它很大,我的意思是如果你经常有15个或更多的字母,那么字典中很大一部分字词可能都适合,而蛮力,逐个检查可能是最有效的算法。另一方面,如果您的列表是商城,如果您通常在游泳池中只有4或5个字母,那么大多数单词都不会有质量,并且有一种方法可以放大候选词。

一种显而易见的方法是将字典放在索引表中。依次从池中取出每个独特的字母,并查找以该字母开头的单词。例如,如果池包含a,b,b,r,o,则查找以a开头的单词,然后是b,然后是r,然后是o。除此之外,我不确定您是否会通过更复杂的数据结构或搜索方案获得更多收益。