【方法一:动态规划】
第一步:确定状态
如果一个子串是回文串,则在其首尾加上相同的字符仍然是回文串。
最后一步:当回文串两头加上不相同的字符时,回文串长度不会增加。
子问题:假设s[i][j]表示从i到j的子串,则判断s[i][j]是否是回文串之前需要检查s[i+1][j-1]是否是回文串
第二步:转移方程
第三步:初始条件与边界值
初始条件:单个字符串的值一定会是回文串,所以对角线上f[i][i]=true
等价于j-i+1<4,等价于收尾去掉没有字符或者收尾去掉只有一个字符的时候,不需要继续检查子串是否是回文,直接设置f[i][j]=true
第四步:运算顺序
计算f[i][j]时,要参考f[i+1][j-1]的值,而f[i+1][j-1]在f[i][j]的左下方,所以不能够行优先遍历,只能列优先。这样才能保证在计算f[i][j]时,左下角的都已经计算过了。
- 收尾字符不相等
- 不是回文串
- 收尾字符相等
- 去掉收尾字符只剩下一个字符或者0个字符,直接可以判断是回文传
- 长度足够,进一步判断子串是否是回文传
string longestPalindrome(string s) {
int n=s.length();
if(n<2)return s;
vector<vector<bool>> f(n, vector<bool>(n,false));
int begin=0;
int maxlen=1;//回文串的长度至少是1
for(int i=0;i<n;i++)f[i][i]=true;//对角线初始化为true
for(int j=1;j<n;j++){//列优先,所以从j开始遍历,f[0][0]和f[n-1][n-1]已经被初始化了
for(int i=0;i<j;i++){
if(s[i]!=s[j])f[i][j]=false;//如果收尾字符不相等,直接判断不是回文串
else {//如果收尾字符相等
if(j-i<3)f[i][j]=true;//去掉收尾字符只剩下1个字符或者0个字符,直接判断是回文串
else f[i][j]=f[i+1][j-1];
}
if(f[i][j]&&j-i+1>maxlen)
{
maxlen=j-i+1;
begin=i;
}
}
}
return s.substr(begin,maxlen);
}
【方法二:中心扩散法】
- 往左寻找与当期位置相同的字符,直到遇到不相等为止。
- 往右寻找与当期位置相同的字符,直到遇到不相等为止。
- 最后左右双向扩散,直到左和右不相等。
- 注意:s.substr(起点,长度)
string longestPalindrome(string s) {
if(s.length()<2)return s;
int left=0,right=0;
int len=1,maxlen=0,maxStart = 0;
for(int i=0;i<s.length();i++)
{
left=i-1;
right=i+1;
while(left>=0&&s[left]==s[i]){
left--;
len++;
}
while(right<s.length()&&s[right]==s[i]){
right++;
len++;
}
while(left>=0&&right<s.length()&&s[left]==s[right]){
left--;
right++;
len+=2;
}
if(len>maxlen){
maxlen=len;
maxStart=left;
}
len=1;
}
return s.substr(maxStart+1,maxlen);
}