将字符串拆分为字典单词

时间:2014-04-24 10:57:39

标签: php algorithm substring

我正在寻找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

3 个答案:

答案 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);
?>