2020-03-19 11:44:44
问题描述:
给定一个字符串 s
,找到 s
中最长的回文子串。你可以假设 s
的最大长度为 1000。
示例 1:
输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd" 输出: "bb"
问题求解:
解法一:DFS
对于长度为n的字符串,回文子串的中心点的个数有2 * n - 1个,其中n个是字符表示回文为奇数的情况,n - 1个是字符之间的位置表示回文为偶数的情况。
可以使用dfs来枚举所有的中心点位置,记录最长的即可。
时间复杂度:O(n ^ 2)
String res = ""; public String longestPalindrome(String s) { int n = s.length(); for (int i = 0; i < n; i++) { extend(s, i, i); extend(s, i, i + 1); } return res; } private void extend(String s, int i, int j) { if (i < 0 || j >= s.length() || s.charAt(i) != s.charAt(j)) return; if (j - i + 1 > res.length()) res = s.substring(i, j + 1); extend(s, i - 1, j + 1); }
解法二:马拉车算法
马拉车算法是解决最长回文子串的最优解,可以在线性时间复杂度内求解。上述的暴力算法有两个问题,一个是要分别讨论奇偶情况,一个是没有充分利用回文的特性。
马拉车算法在每个字符左右都插入一个不常用字符(例如'#',aaa -> #a#a#a#),这样就只有长度为奇数的情况了,因为任意两个连续的字符都不相等。
另外,还需要维护一个目前回文到达的right_most位置,如果当前index在其范围内,可以使用前面计算的结果减少遍历。
时间复杂度:O(n)
public String longestPalindrome(String s) { StringBuffer res = new StringBuffer(); StringBuffer sb = new StringBuffer(); sb.append("#"); for (int i = 0; i < s.length(); i++) { sb.append(s.charAt(i)).append("#"); } int n = sb.length(); int[] dp = new int[n]; dp[0] = 1; int right_most = 0; int mid = 0; for (int i = 1; i < n; i++) { int len = right_most >= i ? Math.min(right_most - i + 1, dp[mid * 2 - i]) : 1; while (i - len >= 0 && i + len <= n - 1 && sb.charAt(i - len) == sb.charAt(i + len)) len += 1; if (i + len - 1 > right_most) { right_most = i + len - 1; mid = i; } dp[i] = len; } int max_len = dp[0]; for (int i = 0; i < n; i++) { if (dp[i] > max_len) { max_len = dp[i]; mid = i; } } for (int i = mid - max_len + 1; i <= mid + max_len - 1; i++) { char c = sb.charAt(i); if (c == '#') continue; res.append(c); } return res.toString(); }