【问题标题】:Finding the longest dictionary word at the end of a string在字符串末尾查找最长的字典单词
【发布时间】:2011-07-21 11:19:21
【问题描述】:

我正在寻找最好的方法来搜索一串字母字符以在字符串末尾找到可能最长的字典单词。

示例:对于字符串qbehugejackhammer,结果应该是jackhammer,而不是hammer

一种更有效地做到这一点的方法是将单词以相反的形式存储在索引表中,并一次迭代一个字母,直到它不再匹配任何内容:

SELECT word FROM dictionary WHERE word LIKE 'remmahkca%';
SELECT word FROM dictionary WHERE word LIKE 'remmahkcaj%'; # last match
SELECT word FROM dictionary WHERE word LIKE 'remmahkcaje%';

这看起来和感觉就像一个 hack,很可能不是最佳解决方案。有没有更快和/或更好的方法来做到这一点?我选择的工具是 PHP 和 MySQL,但如果其他语言或 DBMS 更适合我的需求,我会全力以赴。

【问题讨论】:

  • 最佳方法取决于您打算执行此搜索的次数。你在迭代一个包含几个的结构吗?许多?数百?数千?需要以这种方式检查的单词有多少?
  • 我没有时间给出完整的答案,但请查看en.wikipedia.org/wiki/Trieen.wikipedia.org/wiki/Directed_acyclic_word_graph
  • @Tim:这是一本完整的英语词典,每个活跃的站点用户大约每分钟调用一次。

标签: php mysql optimization string dictionary


【解决方案1】:

一个快速的 hacky 答案:将您的字典加载到 map 或任何 php 等效数据结构中(英文字典只有约 50k 单词,很容易放入 RAM v,并且地图查询要快得多比数据库调用)。然后一次迭代转发 1 个字符,针对映射测试每个子字符串,直到找到匹配项。

根据您的字符串的长度,您可以通过首先检查字典中最长的单词(您可以在字典加载过程中得到它)并开始适当的距离来进行优化。我相信您还有其他类似的优化也可以使用(按起始字符等最长)

编辑:“地图”应该是“设置”。

【讨论】:

  • 哈希映射的 PHP 等价物只是数组,因为键可以是字母数字。我相信字母键在内部被散列并以整数形式使用。通过适当的缓存,这可能是我要采用的方法。此外,根据最长的字典单词检查最大长度是一个非常好的点。
  • @Kaivosukeltaja:实际上我的意思是“set”而不是“map”:你不需要键值结构,只需要一个键结构。虽然如果 php 中没有集合,当然使用地图
【解决方案2】:

这听起来可能有点邪恶,但您可能会通过将字典加载到字典树形状的数组中来获得最佳性能,但是以相反的词序,例如:

array(
    'r' => array(
        'u' => array(), // -- words ending in 'ur' would end up in here
        'a' => array(), // -- words ending in 'ar' would end up here
        'e' => array( // -- words ending in 'er' would end up in here
            'm' => array(
                'm' => array(
                      // -- jackhammer will be kept further up here

然后向上寻找。

$reverseWord = ""; // -- Incoming 'word' string goes here, in reverse.
$dictionary = [structure above];
$dictionaryPosition = $dictionary;
$dictionaryHistory = "";

for( $i = 0, $l = strlen($reverseWord); $i < $l; $i++ ) {
    $char = $reverseWord[$i];

    // -- If this character doesn't exist in this dictionary position, we've reached the end
    if( !isset($dictionaryPosition[$char]) )
        break;

    // -- log this character
    $dictionaryHistory = $char . $dictionaryHistory;

    // -- Climb up the tree
    $dictionaryPosition = $dictionaryPosition[$char];
}

// -- $dictionaryHistory now contains the word you're looking for.

每个数组应包含不超过 26 个条目(假设仅是字母字符),因此您最多可以查找 26*n 个单个字符。即使单词深度为 20 个字符,这也比多次遍历 50k 单词的列表要好得多。

【讨论】:

  • 我很确定这种方法比将所有字典单词存储在哈希表中并对其执行查找要慢。
  • 使用 C 风格的静态大小数组,这肯定会很快。我有点担心 PHP 的数组创建速度以及必须为每个请求重建数组。当然数组可以被序列化和缓存,但我认为反序列化也意味着重新实例化整个 50k 组数组?
【解决方案3】:

您可以从搜索与整个字符串匹配的单词开始,然后不断删除字符串开头的字母,直到找到匹配项:

SELECT word FROM dictionary WHERE word = 'qbehugejackhammer'; --no match
SELECT word FROM dictionary WHERE word = 'behugejackhammer'; --no match
SELECT word FROM dictionary WHERE word = 'ehugejackhammer'; --no match
SELECT word FROM dictionary WHERE word = 'hugejackhammer'; --no match
--...
SELECT word FROM dictionary WHERE word = 'jackhammer'; --found it!

【讨论】:

  • 这是一个很好的方法,但是如果字符串很长,可能会导致很多不必要的查询。将字符串的长度限制为其他答案中建议的最长字典单词的长度当然会消除极长迭代的可能性。
【解决方案4】:

将字典加载到 PHP 数组中。对于每个输入的单词,按照下面的解释,在连续更小的子字符串上使用in_array (link),直到找到匹配项。

例如,考虑您的输入qbehugejackhammer。首先,在数组中搜索qbehugejackhammer,然后搜索behugejackhammer,然后搜索ehugejackhammer,依此类推,直到找到匹配项。您可以在找到第一个匹配项后立即停止。

【讨论】:

  • 输出。使用 in_array 会很慢。而是使用关联数组(word => true)。这样你将有 O(1) 查找而不是 O(n)。
  • 好吧,array_key_exists() 会是更好的选择。另外,就像您指出的那样,字典中的单词应该是键而不是值。
猜你喜欢
  • 2018-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-04
  • 2014-05-04
  • 2013-11-12
相关资源
最近更新 更多