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

Checking if a string contains an English sentence

本文关键字:英语句 句子 英语 包含 字符串 是否 检查      更新时间:2023-10-16

现在,我决定用一本字典来遍历整个过程。每次看到换行符时,我都会创建一个字符串,其中包含从该换行符到下一个换行符的内容,然后我执行string.find()来查看该英文单词是否在其中。这需要很长的时间,每个单词大约需要1/2-1/4秒才能验证。

它运行得很好,但我需要每秒检查数千个单词。我可以运行几个窗口,这不会影响速度(多线程),但它仍然每秒只检查10次。(我需要数千)

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

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

"hithisisastringthatmustbechecked"

但其中大部分都是垃圾,只是随机的字母。

我无法检查不可能的字母组合,因为该字符串会因为"tm"而被抛出,介于"thatsmust"之间。

您可以通过使用Knuth–Morris–Pratt(KMP)算法来加快搜索速度

浏览字典中的每一个单词,并为其建立一个搜索表。你只需要做一次。现在,你对单个单词的搜索将以更快的速度进行,因为"错误开始"将被消除。

有很多快速实现这一目标的策略。

想法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"可能的字母,然后在"b"后面有"i或o"可能的单词。

正则表达式看起来有点像:/(arun)|(b(ill(y+))|(o(b|dy))|。这给出了将其创建为正则表达式的要点。

一旦构建了这个结构,就从第一列开始将其应用于字符串。尝试通过检查备选方案来运行匹配,如果有一个匹配,则尝试向前移动,并尝试箭头及其备选方案后面的字母。如果你到达星号,它就会匹配。如果您用完了备选方案,包括回溯,则转到下一列。

这是一项艰巨的工作,但有时也很方便。

旁注前段时间,我编写了一个程序,编写了直接运行算法的代码,而不是让代码查看二进制树数据结构,从而构建了其中一个。

将每组竖条选项想象成针对特定字符列的switch语句,每个箭头都变成嵌套。如果只有一个选项,则不需要完整的switch语句,只需要一个if

这是一些快速的字符匹配,真的很方便,出于某种原因,我今天无法理解。

Bloom Filter怎么样?

Burton Howard Bloom于1970年构思的Bloom过滤器是用于测试的空间有效概率数据结构元素是否是集合的成员。假阳性匹配为可能,但假阴性是不可能的;即查询返回"套内(可能是错误的)"或"绝对不在套内"。元素可以可以添加到集合中,但不能删除(尽管这可以解决带有"计数"过滤器)。添加到设置,则误报的概率越大。

这种方法的工作原理如下:创建一组要检查的单词(只做一次),然后可以对每个子字符串快速运行"in/not in"检查。如果结果为"不在",则可以安全地继续(Bloom过滤器不会给出假阴性)。如果结果为"in",则运行更复杂的检查进行确认(Bloom过滤器可能会给出误报)。

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

此代码由How to split text without space 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

使用该答案的相同字典并测试字符串输出

>>> infer_spaces("hithisisastringthatmustbechecked")
294.99768817854056

这里的诀窍是找出你可以使用的阈值,记住使用较小的单词会使成本更高(如果算法找不到任何可用的单词,它会返回inf,因为它会将所有单词拆分为单个字母的单词)。

理论上,我认为你应该能够训练一个马尔可夫模型,并使用它来决定字符串可能是一个句子还是垃圾。关于识别单词而不是句子,还有另一个问题:如何确定随机字符串听起来是否像英语?

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

由于你的句子是在没有明确单词边界的情况下拼凑在一起的,这有点棘手,但好消息是马尔可夫模型不关心单词,只关心后面的内容。因此,您可以通过首先从训练数据中剥离所有空间,使其忽略空间。如果你打算用《爱丽丝梦游仙境》作为你的训练文本,第一段可能看起来是这样的:

Alice开始对她在Facebook上的注册感到厌倦,也不想去任何地方或其他地方,她一直在看书,但她正在阅读一些主题或对话,以及书籍的用途,认为Alice没有图片或转换

它看起来很奇怪,但就马尔可夫模型而言,它与经典实现的区别很小。

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