区间dp方法
状态设计
dp[i][j] :s[i~j]是否是回文串
注意:dp[i][j]是bool类型
转移
s[i] == s[j] dp[i][j] = dp[i + 1][j - 1]
两头相同看中间是否是回文串
s[i] != s[j] dp[i][j] = false
两头不同肯定不是
初始化
dp[i][i] = true
长度为1肯定是回文串
dp[i][i + 1] = (s[i] == s[i + 1])
长度为2且字母相同是回文串
地推顺序
小区间 -> 大区间
代码
const int N = 1005;
bool dp[N][N];
class Solution {
public:
string longestPalindrome(string s) {
int n = s.size();
if (n == 1)
return s;
string ans = s.substr(0, 1);
for (int i = 0; i < n; i ++)
dp[i][i] = true;
for (int len = 2; len <= n; len ++) {
for (int i = 0; i + len - 1 < n; i ++) {
int j = i + len - 1;
if (s[i] != s[j])
dp[i][j] = false;
else {
if (len == 2)
dp[i][j] = true;
else
dp[i][j] = dp[i + 1][j - 1];
}
if (dp[i][j] && ans.size() < len)
ans = s.substr(i, len);
}
}
return ans;
}
};
中心扩展法
思路
根据回文串的属性 从它的中点向外扩散的两个元素相同
回文串长度为奇
假设字符串为 “abcba”
回文串长度为偶
假设字符串为 “abba”
这样我们可以枚举中心,然后向两边扩散,扩散到 s[i] != s[j] 那么以这个点为中心的最长回文串我们就找到了 然后在记录一下最长的就行了!是不是比 dp 做法要更简单
代码
const int N = 1005;
bool dp[N][N];
class Solution {
public:
void expend(string s, int L, int R, int &l, int &r) {
while (L >= 0 && R < s.size() && s[L] == s[R])
L --, R ++;
l = L + 1, r = R - 1;
}
string longestPalindrome(string s) {
int n = s.size();
if (n == 1)
return s;
int st = 0, ed = 0; //区间起点和终点
int l1, r1, l2, r2;
for (int i = 0; i < n; i ++) {
expend(s, i, i, l1, r1); //得到长度为奇数的回文串的最大长度
expend(s, i, i + 1, l2, r2); //得到长度为奇数的回文串的最大长度
if (r1 - l1 > ed - st)
st = l1, ed = r1;
if (r2 - l2 > ed - st)
st = l2, ed = r2;
}
return s.substr(st, ed - st + 1);
}
};