方法一:动态规划
class Solution {
public:
string longestPalindrome(string s) {
int n=s.length();
string ans="";
bool dp[n][n];//动态规划表
for(int i=n-1;i>-1;--i){
for(int j=i;j<n;++j){//i是回文串头,j是尾
if (j==i) //单字符是回文串
dp[i][i]=true;
else if(j==i+1) //双字符判断是否相等
dp[i][i+1]=(s[i]==s[i+1]);
else//多字符判断头尾是否相等以及后一位字符长度小2的串是否为回文串
dp[i][j]=(dp[i+1][j-1]&&(s[i]==s[j]));
if(dp[i][j]&&j-i+1>ans.length())
ans=s.substr(i,j-i+1);
}
}
return ans;
}
};
每一长度大于2的子串是否为回文串,都与其后一位字符的结果有关,因此循环可以反着来。
方法二:扩散
class Solution {
public:
string longestPalindrome(string s) {
int begin=0,end=0,ans=0,l=0,n=s.length();
//begin和end是循环中每个回文串的头尾,ans和l是最终得出的最长回文串头和长度。
if(n<2) return s;
for(int i=0;i<n-1;++i){
if(2*i+1<=l||2*(n-i)-1<=l) break;//△
begin=i;end=i;
while(begin-1>=0&&end+1<=n&&s[begin-1]==s[end+1]){
--begin;
++end;
}//判断头尾是否相等,能就扩散,不能就结束循环
if(end-begin+1>l){
l=end-begin+1;
ans=begin;
}
if(s[i+1]==s[i]){
begin=i;
end=i+1;
while(begin>=1&&end+1<=n&&s[begin-1]==s[end+1]){
--begin;
++end;
}
}
if(end-begin+1>l){
l=end-begin+1;
ans=begin;
}
}
return s.substr(ans,l);
}
};
for循环的第一句是重点,参考了分支限界的思想:如果当前子串可能的最长回文串(2i+1和2(n-i)-1中小的那个)都小于已找到的回文串长度,那就可以直接break掉:
当ans从0向n/2移动时,前一字符的可能最长回文串短于后一字符的可能最长回文串,因此不会break;
ans从n/2向n移动时,前一字符的可能最长回文串长于后一字符的可能最长回文串,那么当前一字符满足break条件时,后面的所有字符都会满足。
这样相当程度地提高了效率(12ms)。