题目描述
解法1:动态规划(是否回文用dp数组存储)
1. 定义状态
dp[i][j] 表示子串 s[i…j] 是否为回文子串,这里子串 s[i…j] 定义为左闭右闭区间,可以取到 s[i] 和 s[j]。
2. 状态转移方程
dp[i][j] = (s[i] == s[j]) and dp[i + 1][j - 1]
根据定义可知,j 要严格大于 i(字符串长度为1的不考虑,本题要找的是最长字符串),dp[i][i] = true(即对角线)
边界条件是:表达式 [i + 1, j - 1] 不构成区间,即长度严格小于 2,即 j - 1 - (i + 1) + 1 < 2 ,整理得 j - i < 3。
所以状态转移方程可以优化为:
dp[i][j] = (s[i] == s[j]) and (j - i < 3 or dp[i + 1][j - 1])
或者说,(s[i] == s[j]) and (j - i < 3 or dp[i + 1][j - 1]) 为 true 时, dp[i][j] = true
class Solution {
public:
//不是列出所有情况,用不到DFS回溯
string longestPalindrome(string s) {
int len = s.size();
if(len < 2) return s;
vector<vector<bool> > dp(len, vector<bool>(len, false));
// 状态:dp[i][j] 表示 s[i][j] 是否是回文
// 状态转移方程:dp[i][j] = (s[i] == s[j]) and (j - i < 3 or dp[i + 1][j - 1])
//初始化对角线为 true
for(int i = 0; i < len; i++)
dp[i][i] = true;
int start = 0, max = 1;
for (int right = 1; right < len; right++)
{
for (int left = 0; left < right; left++)
{
if(s[left] == s[right] && (right - left < 3 || dp[left + 1][right - 1]))
{
dp[left][right] = true;
if(right - left + 1 > max)
{
max = right - left + 1;
start = left;
}
}
}
}
return s.substr(start, max);//分割字符串substr(起始位置,分割长度)
}
};
复杂度分析:
时间复杂度:O(N^2)
空间复杂度:O(N^2)
可以再练习一下 【回溯 + 动态规划】LeetCode - 131. 分割回文串 ,同为回文字符串的问题,所求的结果不同,解决的办法也就不同
解法2:中心扩散法判断是否回文(省去dp数组空间)
思路来源于 这里,为了方便日后学习,主要思想截图如下:
具体编码细节在以下的代码的注释中体现:
class Solution {
private:
string centerSpread(string s, int left, int right)
{
// left = right 的时候,此时回文中心是一个空隙,向两边扩散得到的回文子串的长度是奇数
// right = left + 1 的时候,此时回文中心是一个字符,向两边扩散得到的回文子串的长度是偶数
int n = s.size(), i =left, j = right;
while(i >= 0 && j < n && s[i] == s[j])
{
i--; j++;
}
//跳出 while 循环时,恰好满足 s[i] != s[j],因此不能取 i,不能取 j
return s.substr(i + 1, j - i - 1);
}
public:
//不是列出所有情况,用不到DFS回溯
string longestPalindrome(string s) {
int len = s.size();
if(len < 2) return s;
int maxLen = 1;
string ans = s.substr(0, 1);//分割字符串substr(起始位置,分割长度)
// 中心位置枚举到 len - 2 即可
for (int i = 0; i < len - 1; i++)
{
string oddStr = centerSpread(s, i, i);
string evenStr = centerSpread(s, i, i + 1);
string maxLenStr = oddStr.size() > evenStr.size() ? oddStr : evenStr;
if (maxLenStr.length() > maxLen) {
maxLen = maxLenStr.size();
ans = maxLenStr;
}
}
return ans;
}
};
复杂度分析:
时间复杂度:O(N^2)
空间复杂度:O(1),只使用到常数个临时变量,与字符串长度无关。
但是提交结果表明,虽然这样做的运行时间短了,但内存消耗大了。暂时还不理解为什么不用dp二维数组了,反而内存消耗变大了