检查字符串是否包含英语句子
Checking if a string contains an English sentence
现在,我决定用一本字典来遍历整个过程。每次看到换行符时,我都会创建一个字符串,其中包含从该换行符到下一个换行符的内容,然后我执行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"随机乱串"文本)。您只需要训练一次,就可以轻松地将"训练过的"模型保存到磁盘上,并通过从磁盘加载将其重新用于后续运行,这可能需要几秒钟的时间。对字符串进行调用需要少量的浮点乘法才能获得概率,所以在完成训练后,它应该非常快。
- 我的简单if-else语句是如何无法访问的代码
- 有一个打印语句的函数是一种糟糕的编程实践吗
- 线程,如果else语句,都是错误的上下文切换后,会发生什么
- 为什么是0;C++中的有效语句
- Insert函数不适用于2 if语句C++
- If语句未被求值C++
- C++嵌套if语句,基本货币交换
- 多个If语句与使用逻辑运算符计算条件的单个语句的比较
- 是否可以使用if constexpr删除控制流语句
- 如何删除peer if else分支中的冗长句子
- 无法理解此 return 语句的功能,没有它就会发生运行时错误
- 我似乎对if/else的基本语句有问题:/
- 当调用switch语句中的函数时(即使函数不包含循环),似乎是永不结束的循环的问题
- 编译要在英特尔Hyperscan中使用的.cc文件时出现问题
- If语句在c++中被忽略
- 比较if语句中的数组值和int值
- 以在Qt中的IF语句中设置时间延迟
- Craps游戏问题,忽略if语句
- "类模板示例<int>;"语句对 C++11 是什么意思?
- 如何编写一个使用n倍三元条件语句的C++布尔函数