题目:
中心扩散法
我们的暴力解法可能是通过枚举每个子串来判断是否为回文串,然后更新答案,那样一定会有O(n^3)的时间复杂度。
我们换一种角度思考,由于回文串总会有一个中心对称点,这个地方可以是一个数也可以是两个数,如回文串为奇数长则中心为一个字符,回文串为偶数长则中心为两个字符。
通过以上思路可以开始实施我们的中心扩散法。
我们枚举每个回文子串的中心字符,然后通过该字符扩散,不断更新得到最长的那个,而后根据 i 和 len 得到begin。
效率还行
代码
class Solution {
public:
string longestPalindrome(string s) {
if(s.size()<2)
return s;
int maxL = 1;
int begin = 0;
for(int i=0;i<s.size()-1;i++){
//把奇偶两种情形的回文串都包含在内。扩散得到以该字符为中心结点的回文串长度,取尽可能长的那一个。
int oddLen = get_substrLen(s,i,i);
int evenLen = get_substrLen(s,i,i+1);
int maxLen = max(oddLen,evenLen);
//如果此次的maxLen比maxL大,则更新maxL,并随之更新begin。
if(maxLen>maxL){
maxL = maxLen;
//无论该回文串是奇数长还是偶数长都可遵循此规律得出begin。
begin = i - (maxL-1)/2;
}
}
return s.substr(begin,maxL);
}
private:
//用于扩散的函数,通过扩散得出的长度
int get_substrLen(string&s,int left,int right){
int len = 1;
while(left>=0&&right<s.size()){
if(s[left]==s[right]){
len = right - left+1;
left--;
right++;
}
else
break;
}
return len;
}
};
动态规划
这一题的动态规划,完全就是空间换时间的套路。
题目的解题策略还是和暴力法的思路一样,只是把每个已经不可能是回文串的子串通过dp数组的方式存储了起来。人傻了明明一样的时间复杂度,结果效率比中心扩散慢了10倍以上。。
代码
class Solution {
public:
string longestPalindrome(string s) {
if(s.size()<2)
return s;
int maxL = 1;
int begin = 0;
//dp[i][j]代表s[i...j]的回文串是否为回文串。
//那么dp[i][j]的dp关系肯定是dp[i][j] = dp[i-1][j-1],那么我们如何可以在dp[i][j]确定之前先确定dp[i-1][j-1]呢
//可以通过枚举子串长度进行更新各项数据,而不是简单的枚举下标,比如从长度为2的子串开始往上枚举,则到了长度为4的依靠的就是长度为2的子串,长度为5的依靠的是长度为3的子串...等等案例
vector<vector<bool>>dp(s.size(),vector<bool>(s.size()));
//把对角线的dp位置置true,因为dp[i][i]的情况肯定是一个字符,那肯定算是回文串
for(int i=0;i<s.size();i++)
dp[i][i] = true;
for(int L=2;L<=s.size();L++){
//i表示左下标,j代表右下标
for(int i=0;i<s.size()-1;i++){
//通过L和i可以得到右下标j
int j = L+i-1;
//如果j开始越界,则跳出该层循环,因为后面不会再有等于L的子串了
if(j>=s.size())
break;
if(s[i]!=s[j])
dp[i][j] = false;
else{
//当处于相等状况时该串长度小于等于3则表示一定就是回文串了。
if(j-i<3){
dp[i][j] = true;
}
else{
dp[i][j] = dp[i+1][j-1];
}
}
//经过上面更新完dp情况,可以来进行maxL和begin的更新了
if(dp[i][j]&&j-i+1>maxL){
maxL = j-i+1;
begin = i;
}
}
}
return s.substr(begin,maxL);
}
};