给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为1000。
示例 1:
输入: "babad" 输出: "bab" 注意: "aba"也是一个有效答案。
示例 2:
输入: "cbbd" 输出: "bb"
思路:首先给出动态规划解法的时间复杂度O(n^2),需要维护一个二维数组dp[i][j],其中dp[i][j]表示从字符串的s[i]到s[j](包含s[j])是回文序列,dp[i][j]是回文序列,则dp[i][j]赋值为true,我们只需要返回i和j间隔最大的子字符串即可。
这里先给出递归公式:
dp[i, j] = true if i == j = s[i] == s[j] if j = i + 1 = s[i] == s[j] && dp[i + 1][j - 1] if j > i + 1
解释:如果i==j,即中只有一个字符的情况,s[i]当然等于自身,所以赋值为true。如果j=i+1(这里为什么相邻的要单独讨论,因为相邻的没法找到他的子集的情况,及s[i+1]到s[j-1]这种情况,比较特殊,所以单独考虑),如果相等就把dp[i][j]赋值为true。最后一种情况,如果i和j相差3个及以上,就判断他的子集dp[i+1][j-1](想等于向内收缩)以及自身是否相等,如果相等,就把dp[i][j]赋值为true,以下为一个图例说明情况。
由于我们判断的是序列[i,j]是否是回文序列,所以对于左下角部分不用讨论(红色圆圈所示),对二维数组的遍历我们采用红色线所示的路径进行,而不是传统的如下遍历方式:
for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ //blabla } }
因为如下图所示,我们在递归条件式中可以看到,当前状态更新需要用到dp[i+1][j-1]的值,从图像上来看就是需要左下角状态的信息(如图所示,绿色更新需要需要橘黄色的状态作为前提(假设不考虑j-i>2的情况,这里只是示例)),所以我们需要由底向上,由左向右的更新方式。
参考代码:
string longestPalindrome(string s) { string res; bool **dp = new bool *[s.size()]; for (int i = 0; i < s.size(); i++) { dp[i] = new bool[s.size()]{false}; } int n = s.size(); for (int i = n - 1; i >= 0; i--) { for (int j = i; j < n; j++) { dp[i][j] = s[i] == s[j] && ((j - i) < 3 || dp[i + 1][j - 1]); if (dp[i][j] && (res.size() == 0 || (j - i + 1) > res.size())) { res = s.substr(i,j-i+1); } } } for (int i = 0; i < s.size(); i++) { delete[] dp[i]; } delete[] dp; return res; }