我有一大堆单词和短语(词典或词典),其中包括通配符。我需要在一个更小的字符串中找到这些单词和短语的所有实例(目前约150个字符)。
最初,我想反向运行操作;那就是检查我的小字符串中的每个单词是否都存在于Lexicon中,这可以作为哈希表来实现。问题是我的Lexicon中的某些值不是单个单词,而且很多都是通配符(例如substri *)。
我正在考虑使用Rabin-Karp algorithm,但我不确定这是最佳选择。
执行此操作的有效算法或方法是什么?
示例数据:
字典包含数百个单词,可以扩展。这些单词可能以通配符(星号)结尾。以下是一些随机的例子:
我们正在分析的文本(此时)是简短的,非正式的(语法方面的)英语陈述。文本的主要示例(同样,此时)将是Twitter推文。这些仅限于140个字符。例如:
Just got the Google nexus without a contract. Hands down its the best phone
I've ever had and the only thing that could've followed my N900.
虽然注意到我们对此文本执行非常简单的情感分析可能会有所帮助;我们的情感分析技术不我的担忧。我只是将现有解决方案迁移到“实时”处理系统,需要执行一些优化。
答案 0 :(得分:4)
我认为这是Aho-Corasick string-matching algorithm的一个很好的用例,它专门用于在单个字符串中查找大量字符串的所有匹配项。它分两个阶段运行 - 第一阶段,其中创建匹配的自动机(可以提前完成并且仅需要线性时间),以及第二阶段,其中自动机用于查找所有匹配(仅需要线性时间) ,加上与比赛总数成比例的时间)。该算法也可以适用于支持通配符搜索。
希望这有帮助!
答案 1 :(得分:3)
我想抛弃的一个答案是Boyer-Moore搜索算法。它是grep使用的算法。 Grep可能是最快的搜索工具之一。此外,您可以使用GNU Parallel之类的东西让grep并行运行,从而真正加速算法。
此外,这里有一个您可能感兴趣的good article。
答案 2 :(得分:2)
您仍然可以使用原始想法,检查文本中的每个单词与字典。但是,为了运行有效,您需要索引字典,以便快速进行查找。 信息回溯系统中使用的技巧是存储所谓的 permuterm索引(http://nlp.stanford.edu/IR-book/html/htmledition/permuterm-indexes-1.html)。
基本上你要做的是在字典中存储每个可能的单词排列(例如房子):
house$
ouse$h
use$ho
...
e$hous
然后,可以使用此索引快速检查通配符查询。例如,如果您有问题,则可以查看以e$ho
开头的术语的permuterm索引,并且您很快就会找到与房屋匹配的内容。
搜索本身通常使用一些对数搜索策略(二分搜索或b树),因此通常非常快。
答案 3 :(得分:2)
只要模式是完整的单词:您不希望or
与storage
匹配;空格和标点符号是匹配锚点,然后一个简单的方法是将您的词典转换为扫描仪生成器输入(例如,您可以使用flex),生成扫描仪,然后在输入上运行它。
扫描仪生成器用于识别输入中令牌的出现,其中每个令牌类型由正则表达式描述。 Flex和类似程序可以快速创建扫描仪。 Flex默认处理多达8k规则(在您的情况下是词典条目),这可以扩展。生成的扫描仪以线性时间运行,实际上非常快。
在内部,令牌正则表达式在标准的“Kleene定理”管道中进行转换:首先是NFA,然后是DFA。然后将DFA转换为其独特的最小形式。这是在HLL表中编码的,该表在包装器内部发出,该包装器通过引用该表来实现扫描器。这就是flex的作用,但其他策略也是可行的。例如,DFA可以转换为goto
代码,其中DFA状态由代码运行时的指令指针隐式表示。
空间作为锚点的原因是由Flex等程序创建的扫描程序通常无法识别重叠匹配:strangers
无法与strangers
和range
匹配,例如。
这是一个灵活的扫描程序,它匹配您提供的示例词典:
%option noyywrap
%%
"good" { return 1; }
"bad" { return 2; }
"freed"[[:alpha:]]* { return 3; }
"careless"[[:alpha:]]* { return 4; }
"great"[[:space:]]+"loss" { return 5; }
. { /* match anything else except newline */ }
"\n" { /* match newline */ }
<<EOF>> { return -1; }
%%
int main(int argc, char *argv[])
{
yyin = argc > 1 ? fopen(argv[1], "r") : stdin;
for (;;) {
int found = yylex();
if (found < 0) return 0;
printf("matched pattern %d with '%s'\n", found, yytext);
}
}
并运行它:
$ flex -i foo.l
$ gcc lex.yy.c
$ ./a.out
Good men can only lose freedom to bad
matched pattern 1 with 'Good'
matched pattern 3 with 'freedom'
matched pattern 2 with 'bad'
through carelessness or apathy.
matched pattern 4 with 'carelessness'
答案 4 :(得分:1)
这不完全回答算法问题,但请查看re2库。 Python,Ruby和各种其他编程语言都有很好的接口。根据我的经验,它是盲目快速的,并且在我的代码中删除了相当类似的瓶颈,我没有那么大惊小怪,几乎没有额外的代码。
唯一的复杂性来自重叠模式。如果您希望模式从单词边界开始,您应该能够将字典分成r_1, r_2, ..., r_k
形式的一组正则表达式\b(foobar|baz baz\S*|...)
,其中r_{i+1}
中的每个组都有一个前缀r_i
。然后,您可以进行短路评估,因为如果r_{i+1}
匹配,那么r_i
必须匹配。
除非你在高度优化的C中实现你的算法,否则我敢打赌,这种方法比其他任何(算法优越的)答案更快。
答案 5 :(得分:0)
让我直截了当。您有大量查询和一个小字符串,并且您希望在该字符串中查找所有这些查询的实例。
在这种情况下,我建议您将这个小文档编入索引,以便您的搜索时间尽可能短。地狱。使用该文档大小,我甚至会考虑进行小型突变(以匹配通配符等),并将它们编入索引。
答案 6 :(得分:-1)
我的任务非常相似。 这就是我如何解决它,表现令人难以置信 http://www.foibg.com/ijita/vol17/ijita17-2-p03.pdf