我正在寻找PHP中最有效的算法,以检查字符串是否仅由字典单词构成。
示例:
thissentencewasmadefromenglishwords
thisonecontainsyxxxyxsomegarbagexaatoo
pure
thisisalsobadxyyyaazzz
输出:
thissentencewasmadefromenglishwords
pure
a.txt
contains the dictionary words
b.txt
contains the strings: one in every line, without spaces made from a..z chars only
答案 0 :(得分:5)
另一种方法是使用Aho-Corasick string matching algorithm。基本思想是在你的词典中读取并从中创建Aho-Corasick树结构。然后,通过搜索功能运行要分割为单词的每个字符串。
这种方法的优点在于创建树是一次性成本。然后,您可以将它用于您正在测试的所有字符串。搜索函数以O(n)(n是字符串的长度)运行,加上找到的匹配数。这真的很有效率。
搜索功能的输出将是一个字符串匹配列表,告诉您哪些字匹配在哪个位置。
维基百科的文章没有对Aho-Corasick算法做出很好的解释。我更喜欢原始纸张,它非常平易近人。请参阅Efficient String Matching: An Aid to Bibliographic Search。
所以,例如,给出你的第一个字符串:
thissentencewasmadefromenglishwords
你会得到(部分):
this, 0
his, 1
sent, 4
ten, 7
etc.
现在,按位置对匹配列表进行排序。当你从字符串匹配器中获取它时,它将几乎排序,但不完全。
一旦列表按位置排序,您要做的第一件事是确保位置0处存在匹配。如果没有,则字符串未通过测试。如果存在(并且在位置0处可能存在多个匹配),则获取匹配字符串的长度并查看该位置是否存在字符串匹配。添加该匹配的长度,看看下一个位置是否匹配,等等。
如果您测试的字符串不是很长,那么您可以使用类似的强力算法。但是,构建匹配的哈希映射(按位置索引)会更有效。当然,特定位置可能有多个匹配,因此您必须考虑到这一点。但是想看看某个位置是否匹配会非常快。
当然,实现Aho-Corasick算法是一项工作。快速谷歌搜索显示有可用的PHP实现。他们的工作有多好,我不知道。
在一般情况下,这应该非常快。同样,这取决于你的字符串有多长。但是在任何一个位置都有相对较少的比赛你会得到帮助。你可能构造出会出现病态差的运行时的字符串,但你可能不得不尝试一下。而且,如果字符串很短,即使病态情况也不会太长。
答案 1 :(得分:2)
这是一个可以使用Dynamic Programming解决的问题,基于下面的公式:
f(0) = true
f(i) = OR { f(i-j) AND Dictionary.contais(s.substring(i-j,i) } for each j=1,...,i
首先,将文件加载到字典中,然后将DP解决方案用于上述公式。
伪代码类似于:(希望我对索引没有“一个一个”)。
check(word):
f = new boolean[word.length() + 1)
f[0] = true
for i from 1 to word.length() + 1:
f[i] = false
for j from 1 to i-1:
if dictionary.contains(word.substring(j-1,i-1)) AND f[j]:
f[i] = true
return f[word.length()
答案 2 :(得分:1)
我推荐一种递归方法。像这样:
<?php
$wordsToCheck = array(
'otherword',
'word1andother',
'word1',
'word1word2',
'word1word3',
'word1word2word3'
);
$wordList = array(
'word1',
'word2',
'word3'
);
$results = array();
function onlyListedWords($word, $wordList) {
if (in_array($word, $wordList)) {
return true;
} else {
$length = strlen($word);
$wordTemp = $word;
$part = '';
for ($i=0; $i < $length; $i++) {
$part .= $wordTemp[$i];
if (in_array($part, $wordList)) {
if ($i == $length - 1) {
return true;
} else {
$wordTemp = substr($wordTemp, $i + 1);
return onlyListedWords($wordTemp, $wordList);
}
}
}
}
}
foreach ($wordsToCheck as $word) {
if (onlyListedWords($word, $wordList))
$results[] = $word;
}
var_dump($results);
?>