dp
dp[ i ][ j ]代表从i开始到j结束的字符串是否是回文的。
状态转移dp[ i ][ j ] = dp[ i +1][ j - 1 ] && s[i]==s[j]。
时间复杂度O(N*N)
class Solution {
public:
string longestPalindrome(string s) {
if(s.size()==0) return s;
int dp[1005][1005];
memset(dp,0,sizeof(dp));
int len = s.size();
for(int i=1;i<=len;++i) dp[i][i]=1;
for(int i=1;i<len;++i) dp[i][i+1] = s[i]==s[i-1];
for(int k=len-2;k>=1;--k){
int i=1,j=len+1-k;
for(int t=0;t<k;++t){
dp[i][j] = dp[i+1][j-1]&&(s[i-1]==s[j-1]);
++i,++j;
}
}
int ans=0,st;
for(int i=1;i<=len;++i){
for(int j=i;j<=len;++j){
if(dp[i][j] && ans<j-i+1){
ans = j-i+1;
st=i;
}
}
}
return s.substr(st-1,ans);
}
};
马拉车 Manacher
用数组r记录以字符串以每个字符为中心的回文串的半径。(仅考虑奇回文)aba就是010。
从0到n-1,遍历,记录当前最靠右的回文串的右边位置为R,对应回文串的中心位置为C。
现在要得到位置为i的字符的r[i],普通的方法就是让r[i]从0开始慢慢增大直到两边不对称。但可以考虑i之前的结果对i的影响:如果i在C和R之间,那么2C-i(C-(i-C))和i是对称的,如果2C-i的回文串半径小于R-i的话,那么r[i] = r[2C-i],不然r[i]>=R-i。如果是上述情况的第二种,r[i] 只要从R-i开始慢慢增大就可以了。由于R是只增不减的,所以最终时间复杂度为O(N)。
上面的方法只能考虑奇回文,马拉车算法通过在每个字符之间加入‘#’,把奇回文和偶回文都变成了技回文,aba就变成了#a#b#a#,r[i]就是整个回文串的答案。
class Solution {
public:
string longestPalindrome(string s) {
string s2 = "#",ans;
for(auto &c:s) s2+=c,s2+='#';
vector<int> r(s2.size(),0);
int maxr(0),maxi(0);
int C(0),R(0);
for(int i=0;i<s2.size();++i){
if(i<R) r[i] = min(R-i,r[C*2-i]);
while(i-r[i]-1>=0 && i+r[i]+1<s2.size() && s2[i-r[i]-1]==s2[i+r[i]+1]) ++r[i];
if(r[i]+i>R) R=r[i]+i, C=i;
if(r[i]>maxr) maxr=r[i],maxi=i;
}
for(int i=maxi-maxr;i<=maxi+maxr;++i){
if(s2[i]!='#') ans.push_back(s2[i]);
}
return ans;
}
};