检查字符串是否包含英语句子

时间:2013-08-02 17:41:35

标签: c++ string linguistics

到目前为止,我决定拿一本字典并遍历整个事情。每当我看到换行符时,我都会创建一个包含该换行符的字符串到下一个换行符,然后我执行string.find()以查看该英语单词是否在某处。这需要非常长的时间,每个单词大约需要1 / 2-1 / 4秒来验证。

它运作正常,但我需要每秒检查数千个单词。我可以运行几个窗口,它们不会影响速度(多线程),但它仍然只检查10秒钟。 (我需要数千人)

我目前正在编写代码来预编译包含英语中每个单词的大型数组,这应该可以加快速度,但仍然无法达到我想要的速度。 是更好的方法。

我检查的字符串将如下所示:

"hithisisastringthatmustbechecked"

但是大多数都包含完整的垃圾,只是随机字母。

我无法检查不可能的字母组合,因为该字符串会被删除,因为' tm',介于'之间。

5 个答案:

答案 0 :(得分:5)

您可以使用Knuth–Morris–Pratt (KMP) algorithm来加快搜索速度。

浏览每个词典单词,然后build a search table for it。你只需要做一次。现在,您对单个单词的搜索将以更快的速度进行,因为“错误的开始”将被消除。

答案 1 :(得分:1)

有很多策略可以快速完成。

创意1

获取您正在搜索的字符串,并从某个列开始复制每个可能的子字符串并继续整个字符串。然后将每个存储在由它开头的字母索引的数组中。 (如果使用两次字母存储较长的子字符串。

所以数组看起来像这样:

a - substr[0] = "astringthatmustbechecked"
b - substr[1] = "bechecked"
c - substr[2] = "checked"
d - substr[3] = "d"
e - substr[4] = "echecked"
f - substr[5] = null // since there is no 'f' in it
... and so forth

然后,对于字典中的每个单词,搜索由其第一个字母指示的数组元素。这限制了必须搜索的内容的数量。另外,你不可能在第一个' r'之前的任何地方找到一个单词,例如' r'在字符串中。如果这封信完全不存在,有些词甚至不会进行搜索。

创意2

通过记下字典中最长的单词并摆脱长度超过该距离的数组中的字符串来扩展这个想法。

所以你在数组中有这个:

a - substr[0] = "astringthatmustbechecked"

但如果列表中最长的单词是5个字母,则无需保留以下内容:

a - substr[0] = "astri"

如果这封信多次出现,你必须保留更多的信件。所以这个人必须保留整个字符串,因为" e"不断出现不到5个字母。

e - substr[4] = "echecked"

您可以在缩小字符串时使用以任何特定字母开头的最长字词进行扩展。

创意3

这与1和2无关。它可以改为使用。

您可以将字典转换为存储在链接数据结构中的一种正则表达式。也可以编写正则表达式然后应用它。

假设这些是字典中的单词:

arun
bob
bill
billy
body
jose

构建这种链接结构。 (它实际上是一个二叉树,以这种方式表示我可以解释如何使用它。)

a -> r -> u -> n -> *
|
b -> i -> l -> l -> *
|    |              |
|    o -> b -> *    y -> *
|         |
|         d -> y -> *
|
j -> o -> s -> e -> *

箭头表示必须跟随另一个字母的字母。所以" r"必须追随" a"或者它无法匹配。

下降的线表示一个选项。你有" a或b或j"可能的字母,然后是" i或o" " b"。

之后的可能字母

正则表达式看起来有点像:/(arun)|(b(ill(y +))|(o(b | dy)))|(jose)/(虽然我可能已经滑了一下)。这给出了将其创建为正则表达式的要点。

构建此结构后,将其应用于从第一列开始的字符串。尝试通过检查替代方案来运行比赛,如果一个匹配,暂定更多前锋并尝试箭头及其替代后的字母。如果您到达星号/星号,则匹配。如果您的替代品用完了,包括回溯,您将转到下一栏。

这是很多工作,但有时可以很方便。

旁注我通过编写一个编写直接运行算法的代码而不是让代码查看二叉树数据结构的程序来构建其中一个。

将每组垂直条选项视为针对特定字符列的switch语句,并将每个箭头转换为嵌套。如果只有一个选项,则您不需要完整的switch语句,只需if

这是一些快速的角色匹配,并且由于某种原因而非常方便,今天我不知道。

答案 2 :(得分:1)

Bloom Filter怎么样?

  

Burton过滤器由Burton Howard Bloom在1970年构思出来   用于测试的节省空间的概率数据结构   元素是否是集合的成员。误报比赛是   可能,但假阴性不是;即查询返回   “内部设置(可能是错误的)”或“绝对不设置”。元素可以   被添加到集合中,但不被删除(虽然这可以解决   用“计数”过滤器)。添加到的元素越多   设定,误报的可能性越大。

该方法可以如下工作:您创建要检查的单词集(仅执行一次),然后您可以快速运行每个子字符串的“in / not-in”检查。如果结果为“not-in”,则可以继续(Bloom过滤器不会给出错误否定)。如果结果为“in”,则运行更复杂的检查以确认(Bloom过滤器可以给出误报)。

据我所知,一些拼写检查器依赖布隆过滤器来快速测试你的最新单词是否属于已知单词词典。

答案 3 :(得分:0)

理论上,我认为你应该能够训练一个马尔可夫模型并用它来决定一个字符串是一个句子还是可能是垃圾。关于这样做的另一个问题是识别单词,而不是句子:How do I determine if a random string sounds like English?

对句子进行训练的唯一区别是你的概率表会有点大。然而,根据我的经验,现代台式计算机有足够的RAM来处理马尔可夫矩阵,除非你在整个国会图书馆接受培训(这是不必要的 - 即使是不同作者的5本左右的书也应该足以进行非常准确的分类)

由于你的句子被拼凑在一起没有明确的单词边界,这有点棘手,但好消息是马尔可夫模型并不关心单词,而是关注什么。因此,您可以通过首先从训练数据中剥离所有空格来使其忽略空格。如果您打算使用爱丽丝梦游仙境作为您的训练文本,那么第一段也许会如此:

  

alicewasbeginningtogetverytiredofsittingbyhersisteronthebankandofhavingnothingtodoonceortwiceshehadpeepedintothebookhersisterwasreadingbutithadnopicturesorconversationsinitandwhatistheuseofabookthoughtalicewithoutpicturesorconversation

看起来很奇怪,但就马尔可夫模型而言,它与经典实现有一点微不足道。

我看到你关注时间:训练可能需要几分钟(假设你已经编制了金标准“句子”和“随机乱码”文本)。您只需要训练一次,您可以轻松地将“训练”的模型保存到磁盘,并通过从磁盘加载将其重新用于后续运行,这可能需要几秒钟。对字符串进行调用只需要很少量的浮点乘法来获得概率,因此在完成训练后,它应该非常快。

答案 4 :(得分:0)

此代码已从How to split text without spaces into list of words?修改:

from math import log

words = open("english125k.txt").read().split()
wordcost = dict((k, log((i+1)*log(len(words)))) for i,k in enumerate(words))
maxword = max(len(x) for x in words)

def infer_spaces(s):
    """Uses dynamic programming to infer the location of spaces in a string
    without spaces."""

    # Find the best match for the i first characters, assuming cost has
    # been built for the i-1 first characters.
    # Returns a pair (match_cost, match_length).
    def best_match(i):
        candidates = enumerate(reversed(cost[max(0, i-maxword):i]))
        return min((c + wordcost.get(s[i-k-1:i], 9e999), k+1) for k,c in candidates)

    # Build the cost array.
    cost = [0]
    for i in range(1,len(s)+1):
        c,k = best_match(i)
        cost.append(c)

    # Backtrack to recover the minimal-cost string.
    costsum = 0
    i = len(s)
    while i>0:
        c,k = best_match(i)
        assert c == cost[i]
        costsum += c
        i -= k

    return costsum

使用the same dictionary of that answer并测试字符串输出

>>> infer_spaces("hithisisastringthatmustbechecked")
294.99768817854056

这里的技巧是找出你可以使用的阈值,记住使用较小的单词会使成本更高(如果算法找不到任何可用的单词,它会返回inf,因为它会分裂一切都是单字母的单词)。