【LeetCode Hot 100】5. 最长回文子串
考虑回文字符串的特点,从左往右和从右往左读都是一样的,就是说字符串成了“轴对称”。要求一字符串的最长回文子串,我们可以遍历每个字符,求以该字符为轴对称中心的最长对称子串(或者以该字符和下一个字符为中间两个字符的对称子串),在所有这样的子串中长度最长的那个就是我们要找的答案。从回文字符串的对称性来想——这是一种相对直接的想法。
// C++ class Solution { public: pair<int, int> expand(const string& s, int left, int right) { while (left >= 0 && right < s.size() && s[left] == s[right]) { left--; right++; } return {left + 1, right - 1}; } string longestPalindrome(string s) { int start = 0, end = 0; for (int i = 0; i < s.size(); i++) { auto [left1, right1] = expand(s, i, i); auto [left2, right2] = expand(s, i, i + 1); if (right1 - left1 > end - start) { start = left1; end = right1; } if (right2 - left2 > end - start) { start = left2; end = right2; } } return s.substr(start, end - start + 1); } };
回过头来考虑,对于一个子串,如果它是一个回文字符串,那么去除首尾两个字符(当然前提是该子串的长度大于2)它仍然是一个回文子串,反过来说,对于一个回文子串,如果它前后的两个字符相同,那么把这两个新字符加上所形成的新子串也一定是个回文子串。从动态规划的角度去想,我们就找到了这个问题的状态转移方程。如果使用s
的子串
, 是回文子串 ,不是
我们可以写出状态转移方程:
// C++ class Solution { public: string longestPalindrome(string s) { int len = s.length(); if (len < 2) { return s; } // dp[i][j]表示子串[i...j]是否是回文串 vector<vector<int>> dp(len, vector<int>(len)); for (int i = 0; i < len; i++) { dp[i][i] = true; } int begin = 0, max_len = 1; for (int L = 2; L <= len; L++) { for (int i = 0; i < len; i++) { int j = i + L - 1; if (j >= len) { break; } if (s[i] != s[j]) { dp[i][j] = false; } else if (j - i < 3) { dp[i][j] = true; } else { dp[i][j] = dp[i + 1][j - 1]; } if (dp[i][j] && L > max_len) { max_len = L; begin = i; } } } return s.substr(begin, max_len); } };
官方题解中提到,从动态规划法的状态转移方程中可以看出,所有状态在状态转移时的可能性都是唯一的,就是说所有状态都是从某个边界状态扩展得到的,这种所谓边界状态就是子串长度为1或2的状态,从这个角度看,可以得到最开始描述的“中心扩展算法”。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix