返回
探究JS算法之最长回文子串,纵览算法之美
前端
2023-10-11 18:32:49
查找字符串中最长回文子串的算法
回文,一个从左到右读与从右到左读都一样的字符串,就像一面镜子中的倒影。在计算机科学中,查找字符串中最长回文子串是一个经典且实用的问题。本文将深入探讨两种不同的算法,引导你踏上发现最长回文子串的旅程。
动态规划法:自底向上,逐步求解
动态规划算法将问题分解为更小的子问题,依次解决它们。它引入了一个二维表 dp
,其中 dp[i][j]
记录了字符串 s
从位置 i
到位置 j
的子串是否是回文。
算法从左上角开始,逐行逐列填充表格。当遇到 dp[i][j]
时,它检查子串是否回文,如果是则设为 1
,否则设为 0
。
中心扩展法:从中心向两边探索
中心扩展算法以每个字符为中心,向两边扩展回文边界。它有两种情况:
- 奇数长度回文:中心是一个字符。
- 偶数长度回文:中心是两个相邻字符。
对于每种情况,算法从中心向外扩展,直到字符不匹配或到达字符串边界。
代码示例
动态规划法
function longestPalindrome_DP(s) {
if (!s) return "";
let longestPalindrome = "";
let start = 0, end = 0;
const dp = new Array(s.length).fill(0).map(() => new Array(s.length).fill(0));
for (let i = 0; i < s.length; i++) {
dp[i][i] = true;
}
for (let i = s.length - 1; i >= 0; i--) {
for (let j = i + 1; j < s.length; j++) {
if (s[i] === s[j] && (j - i <= 2 || dp[i + 1][j - 1])) {
dp[i][j] = true;
if (j - i + 1 > end - start + 1) {
start = i;
end = j;
}
}
}
}
return s.substring(start, end + 1);
}
中心扩展法
function longestPalindrome_CenterExpansion(s) {
if (!s) return "";
let longestPalindrome = "";
let start = 0, end = 0;
for (let i = 0; i < s.length; i++) {
let [l, r] = [i, i];
while (l >= 0 && r < s.length && s[l] === s[r]) {
if (r - l + 1 > end - start + 1) {
start = l;
end = r;
}
l--;
r++;
}
}
for (let i = 0; i < s.length - 1; i++) {
let [l, r] = [i, i + 1];
while (l >= 0 && r < s.length && s[l] === s[r]) {
if (r - l + 1 > end - start + 1) {
start = l;
end = r;
}
l--;
r++;
}
}
return s.substring(start, end + 1);
}
比较
- 时间复杂度:动态规划法为 O(n^2),中心扩展法也为 O(n^2)。
- 空间复杂度:动态规划法需要额外的空间 O(n^2) 存储表格
dp
,而中心扩展法只需要 O(1) 的额外空间。 - 适用性:中心扩展法更适合处理超长字符串,因为它不会创建巨大的表格。
常见问题解答
-
这些算法只能处理 ASCII 字符吗?
- 不,这些算法可以处理 Unicode 字符。
-
算法是否可以处理重叠的回文?
- 是的,这些算法可以找到所有重叠的回文。
-
是否有更高效的算法?
- 目前还没有已知的时间复杂度低于 O(n^2) 的算法。
-
如何处理有空格或其他分隔符的字符串?
- 可以预处理字符串,删除所有空格和分隔符,然后运行算法。
-
算法是否可以处理多行字符串?
- 可以通过将字符串处理成单行文本,然后运行算法来处理多行字符串。