【问题标题】:How to search fast in list with 40 milions words?如何在 4000 万字的列表中快速搜索?
【发布时间】:2011-04-27 21:26:43
【问题描述】:

如何在包含 4000 万字的列表中快速搜索?

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

示例:列表中有几个单词:

dogging
dopping
baobabisaneviltree

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

结果:

dopping
dogging

(因为,这两个词都包含 'o' 'd' 'i' '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,它的丑陋大:(

【问题讨论】:

  • 在您的字典上创建(和使用)索引将加快您的搜索速度
  • 字符串“iixx”是否与示例匹配?也就是说,包含两次“x”是否意味着搜索字符串中的两个“x”会算作两个匹配的字母?
  • 我相信你的复杂度更高n * m,其中n是列表的长度,m是搜索字符串的长度。
  • @Jeffrey L Whitledge 如果我的特定字符串中有两个 xx(当然是我切到 char 数组的那个字符串),这意味着“可以”这个词有两个 x,但是我想找到包含我的特定字符串中尽可能多的字母的单词。每个使用过的字母都不会再用在那个词里了。
  • @Michal,不,我的意思是它会像 40,000,000 * 8,使用您的单词列表和搜索字符串的假设长度。实际上,你知道吗,更多的是n * m * k,列表长度 * 单词长度 * 搜索字符串长度。但我可能是错的。

标签: c# list dictionary


【解决方案1】:

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

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

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

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

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

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

也就是说,假设你有词典

STOPPING
POTSHARD
OPTING
DECORATE

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

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

现在很容易判断您是否有四个或更多匹配项;您只需对OPSTXZGINOPPST 运行最长公共子序列算法,发现最长公共子序列是OPST,即四个字母,因此匹配。 OPSTXZADHOPRST 的最长公共子序列也是 OPST,所以它可以匹配。 OPSTXYGINOPT的最长公共子序列是OPT,只有三个,OPSTXYACDEEORT的最长公共子序列是OT,只有两个。

假设单词都是短的,我们知道Longest Common Subsequence问题和Sort A Bunch of Characters问题可以很快解决。你只需要这样做 4000 万次就可以了。

现在,如果您想要一个 次线性 解决方案,即在早期从考虑中排除一堆这 4000 万个词汇,那将变得更加困难。您需要次线性解决方案吗?

【讨论】:

  • 更难,但更有趣! :D
【解决方案2】:

有很多方法可以解决这个问题,但在我看来,您可能不得不使用某种索引系统。这些索引将占用与单词本身一样多的内存,并且可能显着更多。

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

当然,这只是打乱了工作,因此需要预先进行大量处理,而不是在搜索时进行。

【讨论】:

  • 编辑:如何在每个字母索引上启动单独的线程?
  • 当然可以进行多线程处理。您可能会考虑为此使用 Parlell 扩展。
【解决方案3】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-22
    • 1970-01-01
    • 1970-01-01
    • 2020-12-17
    • 1970-01-01
    • 2012-05-15
    • 1970-01-01
    相关资源
    最近更新 更多