最长公共子串是连续、相同且长度最大的子串,推荐动态规划解法:用dpi表示以str1[i-1]和str2[j-1]结尾的公共子串长度,字符相等时dpi=dpi-1+1,否则为0,同时记录最大长度和结束位置,最后截取返回。

PHP 中求两个字符串的最长公共子串(Longest Common Substring),核心是找**连续、相同、长度最大**的子串,不是子序列(不要求字符位置连续)。最常用且高效的方法是动态规划,时间复杂度 O(m×n),空间可优化至 O(min(m,n))。
动态规划解法(推荐)
用二维数组 $dp[i][j] 表示以 $str1[i-1] 和 $str2[j-1] 结尾的公共子串长度。若字符相等,则 $dp[i][j] = $dp[i-1][j-1] + 1;否则为 0。过程中记录最大长度和结束位置,最后截取即可。
- 初始化二维数组(或滚动数组优化空间)
- 遍历两字符串所有字符组合,更新 dp 值并维护 max_len 和 end_pos
- 根据 end_pos 和 max_len 从原字符串中提取子串
代码实现(带注释)
// 示例:返回最长公共子串,多个等长时返回第一个
function longestCommonSubstring($str1, $str2) {
if (empty($str1) || empty($str2)) return '';
<pre class="brush:php;toolbar:false;">$len1 = strlen($str1);
$len2 = strlen($str2);
$dp = array_fill(0, $len1 + 1, array_fill(0, $len2 + 1, 0));
$maxLen = 0;
$endPos = 0; // 在 str1 中的结束索引(0-based)
for ($i = 1; $i <= $len1; $i++) {
for ($j = 1; $j <= $len2; $j++) {
if ($str1[$i-1] === $str2[$j-1]) {
$dp[$i][$j] = $dp[$i-1][$j-1] + 1;
if ($dp[$i][$j] > $maxLen) {
$maxLen = $dp[$i][$j];
$endPos = $i - 1;
}
} else {
$dp[$i][$j] = 0;
}
}
}
return $maxLen ? substr($str1, $endPos - $maxLen + 1, $maxLen) : '';}
立即学习“PHP免费学习笔记(深入)”;
// 使用示例 echo longestCommonSubstring("abcdef", "zabcf"); // 输出: "ab"
注意事项与边界情况
该算法区分大小写,空字符串返回空,完全无公共字符时返回空字符串。若需忽略大小写,可提前用 strtolower() 统一处理。注意 PHP 字符串下标是字节级的,对 UTF-8 多字节字符需改用 mb_* 函数(如 mb_substr、mb_strlen)并指定编码,否则中文等可能截断乱码。
- 纯 ASCII 场景直接用 strlen/substr 即可
- 含中文、emoji 等 UTF-8 字符 → 改用 mb_strlen、mb_substr,并在 dp 中用 mb_substr($str, $i, 1) 比较字符
- 只需求长度不关心内容?可省略 endPos 记录,仅维护 maxLen
简单暴力法(仅适合极短字符串)
枚举 str1 的所有子串(O(n²)),用 strpos 或 strstr 判断是否在 str2 中出现(O(m)),总复杂度 O(n²m),不推荐用于长度超 100 的字符串。











