如何快速搜索40个单词的列表?

时间:2011-04-27 21:26:44

标签: c# list dictionary

如何快速搜索包含4千万字的列表?

我需要找到包含至少4个字母的单词,这些字母是我在继续之前指定的。

示例:列表中的字数很少:

dogging
dopping
baobabisaneviltree

我的特定字母为字符串格式'odxxini'。我需要从我的字符串中找到包含任何(4+)字符的任何单词。

结果:

dopping
dogging

(因为,这两个词都包含'o''d''我'n') 我希望我解释得很好。对不起,英语。请纠正错误。

如果有人对这个问题有任何了解,我会很高兴听到他的意见。 :)

我到目前为止写的(因为它是开头......)这段代码:

private void seeksearcher()
        {
            double counter = 0, k=0;
            double licznik = (double)listwords.Capacity;

            char[] letterarray = stringletters.ToCharArray();
            foreach(String word in listwords)
            {

                for(int i=0;i<letterarray.Length;i++)
                    if(word.Contains(letterarray[i]))
                        counter++;
                if(counter > 4)
                    textBox2.Text+=word + Environment.NewLine;

            }
        }

我很确定复杂性现在是n * 7n,它的丑陋大:(

3 个答案:

答案 0 :(得分:12)

首先,显然没有解决方案比解决方案集的大小更快。如果您碰巧有一个匹配词典中每个单词的搜索字符串,那么枚举解决方案集需要枚举词典。

让我们假设每个解集的大小与词典的大小相比非常小。

我们还假设词典中每个条目的大小都很短;你在那里没有任何一万个字母的单词或类似的东西。

鉴于这两个限制,最大的问题是你需要次线性搜索时间吗?

线性时间算法很简单。例如:

  • 将每个词典单词的字符按字母顺序排序。
  • 将查询的字符按字母顺序排序
  • 对已排序的词典中的每个单词进行排序查询的序列比较。

也就是说,假设你有词典

STOPPING
POTSHARD
OPTING
DECORATE

和查询TOPSXZ。按字符排序查询:OPSTXZ。现在浏览词典,按字符排序:

STOPPING --> GINOPPST
POTSHARD --> ADHOPRST
OPTING   --> GINOPT
DECORATE --> ACDEEORT

现在很容易判断你是否有四场或更多场比赛;您只需在OPSTXZGINOPPST上运行最长公共子序列算法,并发现最长公共子序列为OPST,即四个字母,因此匹配。 OPSTXZADHOPRST的最长公共子序列也是OPST,因此匹配。 OPSTXYGINOPT的最长公共子序列是OPT,只有三个,OPSTXYACDEEORT的最长公共子序列是{{1} },这只有两个。

假设单词都很短,我们知道可以快速解决最长公共子序列问题和排序字符串问题。你只需要完成4000万次就可以完成。

现在,如果你想要一个次线性解决方案,你可以从中尽早消除这些4000万个词典中的一堆词,那就更难了。您需要次线性解决方案吗?

答案 1 :(得分:2)

你能提前索引这些词吗?我首先索引单词列表,为每个字符创建一个排序的单词列表:

a: baobabisaneviltree
b: baobabisaneviltree
c: 
d: dogging, dopping
e: etc

然后,对于输入字符串中的每个字母,我将收集匹配的单词,将它们放入字典中,并增加每个单词的找到次数。

dogging: 4
dopping: 4
dapper: 1

然后我会浏览字典,寻找大于4的数字。

如果你无法索引,那么你的解决方案就像你能得到的一样好。您需要查看每个单词中的每个字母(O(n * m)),以查看某个单词是否出现在单词中,然后您需要检查每个单词。您的解决方案的一个问题是,您会多次将该字词添加到文本框中,您可能希望将其添加到if(counter == 4)


代码乐趣(未经测试):

// With 40 million words this can use a lot of space.  You would probably
// want to create the index on disk and maybe the intermediate processing
// as well.
var index = wordList.SelectMany(word => word.ToCharArray(), 
                                (word, character) => 
                                  new { word, character})
                    .ToLookup(x => x.character, x => x.word);
var result = letterArray.Distinct()
                        .SelectMany(c => index[c])
                        .GroupBy(word => word)
                        .Where(word => word.Count() > 4)
                        .Select(word => word.Key);

答案 2 :(得分:2)

有很多方法可以解决这个问题,但是我可能不得不使用某种索引系统。这些索引将占用尽可能多的内容,并且可能比单词本身显着更多的内存。

例如,您可能指向包含字母d的所有单词,然后指向包含字母o的所有单词。等等......然后你会得到一个更小的列表,你可以通过找到你的字母(包含所有你需要的字母的单词)的交集来更容易地搜索。

当然这只是改变了工作,使得它需要预先进行大量处理而不是在搜索时。