LeetCode 5. 最长回文子串
5. 最长回文子串
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-palindromic-substring
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解
最常用的方法应该有三个,暴力解,动态规划和中心扩展,
动态规划
当字符串长度为1的时候,必然是个回文串,那长度为3的情况下呢,就需要s[i] == s[i + 3]的情况下才能形成回文
或者说长度为2时,需要s[i] == s[i + 1]形成回文
so,可以得出个结论,子串从 i 到 j 如果想是个回文,需满足两个条件:
1. 子串[i + 1, j - 1]是个回文
2.字符s[i] == s[j]
SO, 可以得出状态方程
dp[i, j] = dp[i + 1, j - 1] && s[i] == s[j]
用个dp[s.len][s.len]来表示子串从i到j是否是回文串,未必最长回文串;
在dp总,dp[i][i]比如是个回文,因为长度只有1(i - i + 1);
所以可以将dp[i][i]直接先赋值为true,并且作为最初始的判断条件
for (int i = 0; i < len; i++) { dp[i][i] = true; }
进而考虑边界条件dp[i + 1][j - 1]的边界问题:
如果从s[i + 1, j - 1]的长度小于2,既为0 或者1的时候:
如果是0,那必然是回文; 如果是1,则只需要判断s[i] s[j]是否相等即可,可以把这两个判定归类在s[i]==s[j]的情况下,dp[i][j]为回文
代码如下:
string longestPalindrome(string s) { // 特判 int len = s.length(); if (len < 2) { return s; } int maxLen = 1; int begin = 0; // dp[i][j] 表示 s[i, j] 是否是回文串 vector<vector<int>> dp(len, vector<int>(len)); for (int i = 0; i < len; i++) { dp[i][i] = true; } for (int j = 1; j < len; j++) { for (int i = 0; i < j; i++) { printf("str = %s \n", s.substr(i, j - i + 1).c_str()); if (s[i] != s[j]) { dp[i][j] = false; } else { if (j - i < 3) { //(j-1) - (i+1) + 1 < 2 从i到j的内部小于等于1 dp[i][j] = true; } else { dp[i][j] = dp[i + 1][j - 1]; } } // 只要 dp[i][j] == true 成立,就表示子串 s[i..j] 是回文,此时记录回文长度和起始位置 if (dp[i][j] && j - i + 1 > maxLen) { maxLen = j - i + 1; begin = i; } } } return s.substr(begin, begin + maxLen); }
中心扩展
核心思想是对每个字符以及两个字符中间作为中心点,向两边扩展,判定是否是回文,得出最长
为什么是每个字符以及两个字符中间作为中心点呢,因为字符长度可以是基数或者偶数
class Solution { public: pair<int, int> expandAroundCenter(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] = expandAroundCenter(s, i, i); auto [left2, right2] = expandAroundCenter(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); } };
另:马拉车算法
太复杂了,等我看明白先!!!