回文串:如果一个字符串正着读和反着读是一样的,那么它就是一个回文串。例如,a,aba,abba,aaaa等都是回文串。
最长回文子串问题是指,给定一个字符串,求出这个字符串中最长的连续的回文子串。
最长回文子串问题容易与最长回文序列问题混淆,最长回文字串是在给定字符串中连续的回文子串,而最长回文序列问题中可以不连续。例如,给定一个字符串abcbdda,这个字符串的最长回文子串是abc或bcb,而最长回文子序列为abba或adda。
最长回文子串解法一:暴力搜索
对于最长回文子串问题,最容易想到的方法也是最简单粗暴的解法就是,遍历字符串所有子串,并确定每一个子串是否为回文串。一个字符串中,它的字串的数量为O(n^2),遍历每个子串需要O(n)的时间,因此,这个解法的时间复杂度为O(n^3)
最长回文字符串解法二:动态规划
可能有很多人像我一样会想到,这是一个动态规划问题。判断动态规划问题的两个显著条件就是:一.原始问题具有最优子结构。二.子问题的解有重叠
首先我们可以将这个问题分解为子问题,即将一个字符串s分解为O(n^2)个子串,然后判断每个子串是否为一个回文串,并找到最长的回文子串。
假如s[i,j]是一个回文串(i-j>=2),那么s[i+1,j-1]一定也是一个回文串,那么对于子问题:判断s[i,j] (i-j>=2)是否是一个回文串,那么我们首先可以判断s[i+1,j-1]是否为一个回文串,1.如果s[i+1,j-1] 是回文串,那么我们接下来可以根据s[i]是否等于s[j]来判断s[i,j]是否为回文串,2.如果s[i+1,j-1]不是回文串,那么s[i,j]一定也不是回文串。以record[i,j]记录s[i,j]是否为回文串,则有:
string longestPalindrome(string s) {
int length=s.length();
vector<vector<bool>> record(length,vector<bool>(length,false));
for(int i=0;i<length;++i)
record[i][i]=true;
for(int l=1;l<length;++l){ //先解决字符串长度为l+1的最优子问题
for(int i=0;i+l<length;++i){ //i是子字符串起始位置
if(s[i]==s[i+l]){
if(l==1)
record[i][i+l]=true;
else if(record[i+1][i+l-1]==true)
record[i][i+l]=true;
}
}
}
int start=0;
int end=0;
int maxLen=0;
for(int i=0;i<length;++i)
for(int j=i;j<length;++j){
if(record[i][j]==true && j-i+1>maxLen){
start=i;
end=j;
maxLen=j-i+1;
}
}
return s.substr(start,end-start+1);
}
动态规划算法的时间复杂度为O(n^2),空间复杂度为O(n^2)。
那么有没有复杂度更低的算法呢?
最长回文子串解法三:Manacher 算法
Manacher算法讲解的非常详细,时间复杂度为O(n)。
程序如下:
string longestPalindrome(string s){
//Manacher法
int length=s.length();
string pad_s;
pad_s.resize(2*length+1);
for(int i=length-1;i>=0;--i){
pad_s[2*i+2]='#';
pad_s[2*i+1]=s[i];
}
pad_s[0]='*'; //防止在下面的while循环中出界
vector<int> r(2*length+1,0);
int center=0;
int maxStart=0,maxEnd=0,maxLen=0;
for(int i=1;i<2*length+1;++i){
if(center+r[center]>i)
r[i]=min(r[center]-(i-center),r[center-(i-center)]);
else
r[i]=1;
while(pad_s[i+r[i]]==pad_s[i-r[i]])
++r[i];
if(i+r[i]>center+r[center])
center=i;
if(r[i]>maxLen){
maxLen=r[i];
maxStart=i-r[i]+1;
maxEnd=i+r[i]-1;
}
}
string result;
for(int k=maxStart;k<=maxEnd;++k){
if(pad_s[k]!='#')
result.push_back(pad_s[k]);
}
return result;
}