力扣-5-最长回文字串
很明显是动态规划
这类字符串的动规问题一般是定义一个一维dp数组,dp[i]表示:前i个字符中XXX的长度
这里是否适用呢?
假设dp[i]表示前i个字符(包括i)中最长回文字符串的长度,能找到状态转移方程吗?
感觉有点麻烦,关键是这个回文的判断与递增长度的关系
看一眼题解
这里的动态规划没有直接着眼于最长回文子串长度的状态转移
而是对于回文串判断的状态转移:如果字符[i,j]是一个回文串,那么字符[i+1,j-1]是回文串,且字符i==j
但是这个怎么…或许不是像最上面那样,而是去扫描每个字符,但后按照上面的状态转移方程判断最长(向两边拓宽),同时动态更新最长长度,或许还能做一点剪枝
试着写写吧
string longestPalindrome(string s) {
// 如何保存最长的结果并返回呢?
int maxLen = 1,startIndex;
// 排除单字符,s[0]肯定不是个回文串,同理s[s.size()-1]也不会是
for (int i = 1; i < s.size() - 1; i++) {
int j = i - 1, k = i + 1;
while (j >= 0 && k <= s.size()) {
if (s[j] == s[k]) {
maxLen = max(maxLen, k - j + 1);
startIndex = j;
j--;
k++;
}
else break;
}
}
string str;
for (int i = startIndex; i < startIndex + maxLen; i++) str += s[i];
return str;
}
这是我写出的第一版代码,但是有个致命问题,就是没考虑到aabb这种双数的情况
因为像aa这种情况只会出现在第一次初始化的情况
string longestPalindrome(string s) {
// 如何保存最长的结果并返回呢?
int maxLen = 1,startIndex;
// 排除单字符,s[0]肯定不是个回文串,同理s[s.size()-1]也不会是
// 这么写,如果有存在长度一样的回文子串,后面的会覆盖前面的结果
// 这里没考虑到aabb这样双数的情况
for (int i = 1; i < s.size() - 1; i++) {
// 因为上面的范围设定,所以出事的j,k一定是合法的
int j = i - 1, k = i + 1;
if (s[j] != s[k]) {
if (s[j] == s[i]) k = i;
else if (s[i] == s[k]) j = i;
}
while (j >= 0 && k <= s.size()) {
if (s[j] == s[k]) {
maxLen = max(maxLen, k - j + 1);
startIndex = j;
j--;
k++;
}else break;
}
}
string str;
for (int i = startIndex; i < startIndex + maxLen; i++) str += s[i];
return str;
}
这是第二版的代码,我以为我解决了aabb的问题,但是没考虑好初始化,比如a
`for (int i = 1; i < s.size() - 1; i++)`这里for循环的写法要求长度至少为3
那么就要额外考虑长度为1,和2的情况
```C++
string longestPalindrome(string s) {
if (s.size() == 1) return s;
else if (s.size() == 2) {
if (s[0] == s[1]) return s;
else {
string s(1, s[0]);
return s;
}
}
// 如何保存最长的结果并返回呢?
int maxLen = 1,startIndex;
// 排除单字符,s[0]肯定不是个回文串,同理s[s.size()-1]也不会是
// 这么写,如果有存在长度一样的回文子串,后面的会覆盖前面的结果
// 这里没考虑到aabb这样双数的情况
for (int i = 1; i < s.size() - 1; i++) {
// 因为上面的范围设定,所以出事的j,k一定是合法的
// 但要是这么写,长度至少为3
int j = i - 1, k = i + 1;
if (s[j] != s[k]) {
if (s[j] == s[i]) k = i;
else if (s[i] == s[k]) j = i;
}
while (j >= 0 && k <= s.size()) {
if (s[j] == s[k]) {
maxLen = max(maxLen, k - j + 1);
startIndex = j;
j--;
k++;
}else break;
}
}
string str;
for (int i = startIndex; i < startIndex + maxLen; i++) str += s[i];
return str;
}
很不优雅地补了一段,还是不对,aaaa的情况不对
思路乱了,要考虑核长度1和核长度2向两边拓展的情况
中心拓展法
再瞄一眼题解
其实到目前已经不是动态规划了,而是中心拓展,另外就是这里考虑到中心长度有1和2两种情况,所以要单独抽出一个向两边拓展的函数
pair<int, int> expand(string s, int i, int j) {
while(i>=0&&j<s.size()&&s[i]==s[j]){
--i;
++j;
}
return { i+1,j-1 };
}
string longestPalindrome(string s) {
int start = 0, end = 0;
for (int i = 0; i < s.size(); ++i) {
pair<int,int> p1 = expand(s, i, i);
pair<int, int> p2 = expand(s, i, i + 1);// 这里返回值成长度可能为负
if (p1.second - p1.first > end - start) {
start = p1.first;
end = p1.second;
}
if (p2.second - p2.first > end - start) {
start = p2.first;
end = p2.second;
}
}
return s.substr(start, end - start + 1);
}
动态规划
还是要用动态规划做一遍
马拉车算法
参考: