如何在 PHP 中找到两个字符串之间的最大公共子字符串?

Posted

技术标签:

【中文标题】如何在 PHP 中找到两个字符串之间的最大公共子字符串?【英文标题】:How can I find the Largest Common Substring between two strings in PHP? 【发布时间】:2010-09-25 02:18:13 【问题描述】:

有没有一种快速算法可以在两个strings 中找到最大公共子串,还是 NPComplete 问题?

php 中我可以大海捞针:

<?php

if (strstr("there is a needle in a haystack", "needle")) 
    echo "found<br>\n";

?>

我想我可以在strings 之一上循环执行此操作,但这会非常昂贵!特别是因为我的应用是搜索电子邮件数据库并查找垃圾邮件(即同一个人发送的类似电子邮件)。

有没有人可以扔掉任何 PHP 代码?

【问题讨论】:

【参考方案1】:

迟到了,但这里有一种方法可以在字符串数组中找到最大的公共子字符串:

例子:

$array = array(
    'PTT757LP4',
    'PTT757A',
    'PCT757B',
    'PCT757LP4EV'
);
echo longest_common_substring($array); // => T757

功能:

function longest_common_substring($words) 
    $words = array_map('strtolower', array_map('trim', $words));
    $sort_by_strlen = create_function('$a, $b', 'if (strlen($a) == strlen($b))  return strcmp($a, $b);  return (strlen($a) < strlen($b)) ? -1 : 1;');
    usort($words, $sort_by_strlen);
    // We have to assume that each string has something in common with the first
    // string (post sort), we just need to figure out what the longest common
    // string is. If any string DOES NOT have something in common with the first
    // string, return false.
    $longest_common_substring = array();
    $shortest_string = str_split(array_shift($words));

    while (sizeof($shortest_string)) 
        array_unshift($longest_common_substring, '');
        foreach ($shortest_string as $ci => $char) 
            foreach ($words as $wi => $word) 
                if (!strstr($word, $longest_common_substring[0] . $char)) 
                    // No match
                    break 2;
                 // if
             // foreach
            // we found the current char in each word, so add it to the first longest_common_substring element,
            // then start checking again using the next char as well
            $longest_common_substring[0].= $char;
         // foreach
        // We've finished looping through the entire shortest_string.
        // Remove the first char and start all over. Do this until there are no more
        // chars to search on.
        array_shift($shortest_string);
    
    // If we made it here then we've run through everything
    usort($longest_common_substring, $sort_by_strlen);
    return array_pop($longest_common_substring);

我已经在我的博客上写了一点:

Find the longest common substring using PHP(2011 年 2 月 24 日)

【讨论】:

这个函数会将输出小写!!被警告。对于这个问题,还有其他算法不会遇到这个问题。【参考方案2】:

我刚刚写了一个函数,它在 str1 中找到 str2 中存在的最长子字符串

public static function getLongestMatchingSubstring($str1, $str2)

    $len_1 = strlen($str1);
    $longest = '';
    for($i = 0; $i < $len_1; $i++)
        for($j = $len_1 - $i; $j > 0; $j--)
            $sub = substr($str1, $i, $j);
            if (strpos($str2, $sub) !== false && strlen($sub) > strlen($longest))
                $longest = $sub;
                break;
            
        
    
    return $longest;

【讨论】:

这不如动态编程方法 (en.wikibooks.org/wiki/Algorithm_Implementation/Strings/…) 快,但它使用的内存要少得多。在我的测试中,DP 方法在比较两个 1200 个字符的字符串时使我的 PHP 崩溃。即使我分配更多内存,对于相同的作业,这也只会慢 6 倍(6 秒对 1 秒)。 在我的基准测试中,这个实现可以达到 1000 !!!和其他算法一样慢(尤其是长字符串)。请注意。【参考方案3】:

我后来找到了a relevant wikipedia article。这不是一个 NP 完全问题,使用动态规划算法可以在 O(mn) 时间内完成。

在 PHP 中,我发现 similar_text 函数非常有用。这是一个代码示例,用于检索一系列文本电子邮件并遍历它们并找到彼此之间 90% 相似的电子邮件。 注意:这样的东西不可扩展

<?php
// Gather all messages by a user into two identical associative arrays
$getMsgsRes = mysql_query(SELECT * FROM email_messages WHERE from = '$someUserID');
while($msgInfo = mysql_fetch_assoc($getMsgsRes))

    $msgsInfo1[] = $msgInfo;
    $msgsInfo2[] = $msgInfo;


// Loop over msgs and compare each one to every other
foreach ($msgsInfo1 as $msg1)
    foreach ($msgsInfo2 as $msg2)
        similar_text($msg1['msgTxt'],$msg2['msgTxt'],$similarity_pst);
        if ($similarity_pst > 90)
            echo "$msg1['msgID'] is $similarity_pst% to $msg2['msgID']\n";
?>

【讨论】:

答案本身并没有帮助,但这个想法解决了我的问题。使用similar_text()查找字符串相似度的完美示例【参考方案4】:

请查看维基教科书上的Algorithm implementation/Strings/Longest common substring。我还没有测试过 PHP 实现,但它似乎与 Wikipedia 页面上的通用算法相匹配。

【讨论】:

它也非常慢。*** Longest_common_substring_problem 页面上列出的动态编程算法非常节省空间,但在 php 中实现时,其速度是编写良好的蛮力解决方案的两倍多,例如@Chrisbloom7 解决方案如下。【参考方案5】:

similar_text 函数可能是你想要的。

这会计算两个字符串之间的相似度。返回两个字符串中匹配字符的数量

你可能还想看看levenshtein

【讨论】:

不,这不是他想要的。那些算法根本不计算最长公共子串,你为什么还要建议这个?【参考方案6】:

特别是因为我的应用是搜索电子邮件数据库并查找垃圾邮件(即同一个人发送的类似电子邮件)。

我认为您应该研究贝叶斯垃圾邮件推理算法,不一定是最长公共子字符串。

http://www.devshed.com/c/a/PHP/Implement-Bayesian-inference-using-PHP-Part-1/

【讨论】:

以上是关于如何在 PHP 中找到两个字符串之间的最大公共子字符串?的主要内容,如果未能解决你的问题,请参考以下文章

最长公共子串与最长公共子序列之间的关系

最长公共子词错误结果

LeetCode#583-两个字符串的删除操作-求最大公共子序列

R:从开头找到最大的公共子串

在Python中找到两个给定路径之间的公共文件的有效方法

如何找到树上一组节点之间的最大距离?