力扣-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); }
动态规划
还是要用动态规划做一遍
马拉车算法
参考:
本文作者:YaosGHC
本文链接:https://www.cnblogs.com/yaocy/p/16827282.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步