—— write for my baby, mua
[题目]
Given a string S, find the longest palindromic substring in S. You may assume that the maximum length ofS is 1000, and there exists one unique longest palindromic substring.
[中文翻译]
给定一个字符串S,求S的最长回文字串。输入保证S的长度小于等于1000,且只有一个唯一的最长回文字串。
[解题思路]
回文子串的长度可能是奇数也可能是偶数,这里有一个tips,在原串头尾和每两个字母间插入‘#’,求新串的最长回文子串,去掉‘#’之后的串即为原串的最长回文子串。
新串的最长回文子串长度必为奇数。
事实上,插入的符号‘#’不需要是特殊字符,任意的字符(包括原串中出现的字符)都是可以的。
以下的算法均为求新串的最长回文子串。
O(KN) K=len(longest palindromic substring)
依次遍历字符串,将遍历到的字符作为回文的中间字符,向两边扩展,直到不满足回文条件。记录当前最长子串的中间字符下标和扩展长度,在遍历的过程中更新。
O(kN) k<len(longest palindromic substring)
根据回文的性质,在遍历的过程中,计算当前字符为中心的最长回文串时,可利用之前的计算结果,如下图。
图1. 利用之前计算结果示意图
L1和L2分别是以index和j为中心的最长回文串。i是当前遍历到的中心字符,j是i关于index的对称点。根据回文的性质,则L3必定是一个回文串。
下面,进行详细地讨论L1确定的情况下的各种情况。
一、 i处于L1之外
图2. 情况一-i处于L1之外
对于i处于L1之外的情况,不能利用j处的信息,因此i处最长回文需要从中心开始扩展。
二、 i处于L1内
对于i处于L1内的情况,分三种情况讨论。
1. L2⊊L1
图3. 情况二-i处于L1内-1-L2⊊L1
L2⊊L1,因为L2是j处的最长回文子串,则i处最长回文子串的长度即为L2的长度。
2. L2⊂L1,且L2的左边界正好为L1的左边界
图4. 情况二-i处于L1内-2-L2⊂L1且L1、L2左边界重合
L3实线部分与L2对称,必定是回文子串,但因为右边黑色格子的信息未知,所以L3实线部分未必是最长回文子串。因此可以从L3实线部分开始扩展。
3. L2-L1≠∅
图5. 情况二-i处于L1内-2-(L2-L1≠∅)
当L2的左边界小于L1时,i处的最长回文子串必定为L3,因为右边蓝色格子一定不等于左边橙色格子,否则L1就不是index处的最长回文子串了。
在程序中,可以用一个数组max[i]记录以i为中心的最长回文子串单边长度,来保存L2的信息。index和L1可用两个整数index和单边长度mx表示。
现在,需要考虑的是L1如何确定。有两个策略。
1. 以当前出现过的最长回文子串作为L1。
2. 以当前出现过的右边界最大的回文子串作为L1。
这两个策略都不是最优策略。因为我们的目标是尽可能地利用之前的信息,而这两个策略都存在例子不能充分地利用信息。一般情况下,策略2较优。
最后找到最长回文子串后,需要把预处理的‘#’去掉。
[C++代码]
class Solution {
public:
string longestPalindrome(string s) {
string ss = "#";
int max[2005];
int index, mx;
for (int i = 0; i < s.size(); i++)
ss = ss + s.at(i) + "#";
max[0] = 1;
index = 0; mx = 1;
for (int i = 1; i < ss.size(); i++) {
int j = index + index - i;
int leftBound = index - mx;
if (j <= leftBound) {
max[i] = 1;
int kl = i - 1, kr = i + 1;
while (kl>=0 && kr < ss.size() && ss.at(kl) == ss.at(kr)) {
max[i]++;
kl--; kr++;
}
}
else {
if ((j - max[j]) > leftBound)
max[i] = max[j];
else if ((j - max[j]) == leftBound) {
max[i] = max[j];
int kl = i - max[i], kr = i + max[i];
while (kl >= 0 && kr < ss.size() && ss.at(kl) == ss.at(kr)) {
max[i]++;
kl--; kr++;
}
}
else
max[i] = index + mx - i;
}
if (i + max[i] >= index + mx) {
index = i;
mx = max[i];
}
}
index = 0; mx = 1;
for (int i=1; i<ss.size(); i++)
if (max[i] > mx) {
index = i;
mx = max[i];
}
int l = index - mx + 2;
int r = index + mx;
string res = "";
while (l < r) {
res += ss.at(l);
l += 2;
}
return res;
}
};