Longest Palindromic Substring

描述

Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.

分析

最长回文子串,非常经典的题。

思路一:暴力枚举,以每个元素为中间元素,同时从左右出发,复杂度O(n^2)

思路二:记忆化搜索,复杂度O(n^2)。设f[i][j] 表示[i,j]之间的最长回文子串,递推方程如下:

  1. f[i][j] = if (i == j) S[i]
  2. if (S[i] == S[j] && f[i+1][j-1] == S[i+1][j-1]) S[i][j]
  3. else max(f[i+1][j-1], f[i][j-1], f[i+1][j])

思路三:动规,复杂度O(n^2)。设状态为f(i,j),表示区间[i,j]是否为回文串,则状态转移方程为

f(i,j)={true,i=jS[i]=S[j],j=i+1S[i]=S[j] and f(i+1,j1),j>i+1f(i,j)=\begin{cases}true & ,i=j\S[i]=S[j] & , j = i + 1 \S[i]=S[j] \text{ and } f(i+1, j-1) & , j > i + 1\end{cases}

思路四:Manacher’s Algorithm, 复杂度O(n)。详细解释见 http://leetcode.com/2011/11/longest-palindromic-substring-part-ii.html

备忘录法

  1. // Longest Palindromic Substring
  2. // 备忘录法,会超时
  3. // 时间复杂度O(n^2),空间复杂度O(n^2)
  4. public class Solution {
  5. private final HashMap<Pair, String> cache = new HashMap<>();
  6. public String longestPalindrome(final String s) {
  7. cache.clear();
  8. return cachedLongestPalindrome(s, 0, s.length() - 1);
  9. }
  10. String longestPalindrome(final String s, int i, int j) {
  11. final int length = j - i + 1;
  12. if (length < 2) return s.substring(i, j + 1);
  13. final String s1 = cachedLongestPalindrome(s, i + 1, j - 1);
  14. if (s1.length() == length - 2 && s.charAt(i + 1) == s.charAt(j - 1))
  15. return s.substring(i, j + 1);
  16. final String s2 = cachedLongestPalindrome(s, i + 1, j);
  17. final String s3 = cachedLongestPalindrome(s, i, j - 1);
  18. // return max(s1, s2, s3)
  19. if (s1.length() > s2.length()) return s1.length() > s3.length() ? s1 : s3;
  20. else return s2.length() > s3.length() ? s2 : s3;
  21. }
  22. String cachedLongestPalindrome(final String s, int i, int j) {
  23. final Pair key = new Pair(i, j);
  24. if (cache.containsKey(key)) {
  25. return cache.get(key);
  26. } else {
  27. final String result = longestPalindrome(s, i, j);
  28. cache.put(key, result);
  29. return result;
  30. }
  31. }
  32. // immutable
  33. static class Pair {
  34. private int x;
  35. private int y;
  36. public Pair(int x, int y) {
  37. this.x = x;
  38. this.y = y;
  39. }
  40. @Override
  41. public int hashCode() {
  42. return x * 31 + y;
  43. }
  44. @Override
  45. public boolean equals(Object other) {
  46. if (this == other) return true;
  47. if (this.hashCode() != other.hashCode()) return false;
  48. if (!(other instanceof Pair)) return false;
  49. final Pair o = (Pair) other;
  50. return this.x == o.x && this.y == o.y;
  51. }
  52. }
  53. }

动规

  1. // Longest Palindromic Substring
  2. // 动规,时间复杂度O(n^2),空间复杂度O(n^2)
  3. class Solution {
  4. public String longestPalindrome(final String s) {
  5. final int n = s.length();
  6. final boolean[][] f = new boolean[n][n];
  7. int maxLen = 1, start = 0; // 最长回文子串的长度,起点
  8. for (int i = 0; i < n; i++) {
  9. f[i][i] = true;
  10. for (int j = 0; j < i; j++) { // [j, i]
  11. f[j][i] = (s.charAt(j) == s.charAt(i) &&
  12. (i - j < 2 || f[j + 1][i - 1]));
  13. if (f[j][i] && maxLen < (i - j + 1)) {
  14. maxLen = i - j + 1;
  15. start = j;
  16. }
  17. }
  18. }
  19. return s.substring(start, start + maxLen);
  20. }
  21. }

Manacher’s Algorithm

  1. // Longest Palindromic Substring
  2. // Manacher’s Algorithm
  3. // 时间复杂度O(n),空间复杂度O(n)
  4. class Solution {
  5. // Transform S into T.
  6. // For example, S = "abba", T = "^#a#b#b#a#$".
  7. // ^ and $ signs are sentinels appended to each end to avoid bounds checking
  8. public String preProcess(final String s) {
  9. int n = s.length();
  10. if (n == 0) return "^$";
  11. StringBuilder ret = new StringBuilder("^");
  12. for (int i = 0; i < n; i++) ret.append("#" + s.charAt(i));
  13. ret.append("#$");
  14. return ret.toString();
  15. }
  16. String longestPalindrome(String s) {
  17. String T = preProcess(s);
  18. final int n = T.length();
  19. // 以T[i]为中心,向左/右扩张的长度,不包含T[i]自己,
  20. // 因此 P[i]是源字符串中回文串的长度
  21. int[] P = new int[n];
  22. int C = 0, R = 0;
  23. for (int i = 1; i < n - 1; i++) {
  24. int iMirror = 2 * C - i; // equals to i' = C - (i-C)
  25. P[i] = (R > i) ? Math.min(R - i, P[iMirror]) : 0;
  26. // Attempt to expand palindrome centered at i
  27. while (T.charAt(i + 1 + P[i]) == T.charAt(i - 1 - P[i]))
  28. P[i]++;
  29. // If palindrome centered at i expand past R,
  30. // adjust center based on expanded palindrome.
  31. if (i + P[i] > R) {
  32. C = i;
  33. R = i + P[i];
  34. }
  35. }
  36. // Find the maximum element in P.
  37. int maxLen = 0;
  38. int centerIndex = 0;
  39. for (int i = 1; i < n - 1; i++) {
  40. if (P[i] > maxLen) {
  41. maxLen = P[i];
  42. centerIndex = i;
  43. }
  44. }
  45. final int start =(centerIndex - 1 - maxLen) / 2;
  46. return s.substring(start, start + maxLen);
  47. }
  48. }

原文: https://soulmachine.gitbooks.io/algorithm-essentials/content/java/string/longest-palindromic-substring.html