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.
https://leetcode.com/problems/longest-palindromic-substring/
求最大回文的长度,其实这道题比上一道有意思。
方法1 循环查询 (该方案为O(N*N*N))
public static boolean isPalindrome(String s, int start, int end) { if (((end - start) & 0x1) == 1) { while (start + 1 != end) { if (s.charAt(start++) != s.charAt(end--)) { return false; } } return s.charAt(start) == s.charAt(end); } else { while (start != end) { if (s.charAt(start++) != s.charAt(end--)) { return false; } } } return true; } public static String longestPalindrome_1(String s) { int len = s.length(); if (len < 2) { return s; } for (int i = 0, end=len/2; i < end; i++) { for (int j = len - 1, k = i; k > -1; j--, k--) { if (k == j && j == i) { return ""; } if (isPalindrome(s, k, j)) { return s.substring(k, j + 1); } } } return ""; }
方法2 动态规划 (该方案为O(N*N))
由于没学过动态规划,特意去学习了一下
/** * * 1. 初始条件: 空串 看作是回文的最初始条件,LP[i][i-1]=1。这作为初始状态,并不认为是有回文。 单字符串 是直接认为有回文的,LP[i][i]=1。 2. 状态转移: 若LP[i][j]=1且a[i-1]==a[j+1] ,那么有LP[i-1][j+1]=1,否则LP[i-1][j+1]=0 * @param s * @return */ public static String longestPalindromeDP_2(String s) { int n = s.length(); int longestBegin = 0; int maxLen = 1; boolean[][] table = new boolean[n][n]; // 单字符 for (int i = 0; i < n; i++) { table[i][i] = true; } // 双字符 for (int i = 0; i < n - 1; i++) { if (s.charAt(i) == s.charAt(i + 1)) { table[i][i + 1] = true; longestBegin = i; maxLen = 2; } } // 子串长度 for (int len = 3; len <= n; len++) { // 子串的起始位置 for (int i = 0; i < n - len + 1; i++) { // 子串的结束位置 int j = i + len - 1; // DP条件 if (table[i + 1][j - 1] && s.charAt(i) == s.charAt(j) ) { table[i][j] = true; longestBegin = i; maxLen = len; } } } return s.substring(longestBegin, longestBegin + maxLen); }
方法3 中心扩展,方法1的优化版本 (该方案为O(N*N))
static class Pair { int s; int e; public Pair(int s, int e) { this.s = s; this.e = e; } int length() { return e - s + 1; } } public static Pair expandAroundCenter2(String s, int c1, int c2) { int l = c1, r = c2; int n = s.length(); while (l > -1 && r < n && s.charAt(l) == s.charAt(r)) { l--; r++; } return new Pair(l + 1, r); } public static String longestPalindromeSimple2(String s) { int n = s.length(); if (n == 0) return ""; Pair longest = new Pair(0, 1); // a single char itself is a // palindrome int i = 0; // 偶回文 Pair p2 = expandAroundCenter2(s, i, i + 1); if (p2.length() > longest.length()) longest = p2; for (i = 1; i < n - 1; i++) { // 奇回文 Pair p1 = expandAroundCenter2(s, i, i); if (p1.length() > longest.length()) longest = p1; // 偶回文 p2 = expandAroundCenter2(s, i, i + 1); if (p2.length() > longest.length()) longest = p2; } return s.substring(longest.s,longest.e); }
方法.后缀数组, logN * O(n)
方法5.Manacher算法, O(n)
// ^ and $ 避免空指针 static StringBuilder preProcess(String s) { int n = s.length(); StringBuilder buff = new StringBuilder("^"); for (int i = 0; i < n; i++) { buff.append("#").append(s.charAt(i)); } buff.append("#$"); return buff; } public static String longestPalindrome(String s) { if (s.length() < 2) { return s; } // 插入到^#c#a#b#b#a#$ StringBuilder T = preProcess(s); int length = T.length(); int[] p = new int[length]; //存储每一个位置的长度 int C = 0, R = 0; for (int i = 1; i < length - 1; i++) { int i_mirror = C - (i - C); int diff = R - i; // prettyPrint(T, C, R, i, i_mirror, p); if (diff >= 0)// 当前i在C和R之间,可以利用回文的对称属性 { // R 能移动已经判断是相等过 if (p[i_mirror] < diff)// i的对称点的回文长度在C的大回文范围内部 { p[i] = p[i_mirror]; // System.out.println(T.charAt(i_mirror) + "<<$>>"+ T.charAt(i)); } else { p[i] = diff; // i处的回文可能超出C的大回文范围了 while (T.charAt(i + p[i] + 1) == T.charAt(i - p[i] - 1)) { p[i]++; } C = i; R = i + p[i]; } } else { p[i] = 0; while (T.charAt(i + p[i] + 1) == T.charAt(i - p[i] - 1)) { p[i]++; } C = i; R = i + p[i]; } } int maxLen = 0; int centerIndex = 0; // 最大的索引 for (int i = 2; i < length - 1; i+=1) { if (p[i] > maxLen) { maxLen = p[i]; centerIndex = i; } } // 计算起始地址 centerIndex = (centerIndex - 1 - maxLen) / 2; return s.substring(centerIndex, centerIndex + maxLen); }
时间: 2024-10-24 12:54:47