如何在 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 中找到两个字符串之间的最大公共子字符串?的主要内容,如果未能解决你的问题,请参考以下文章