Leetcode: Longest Palindromic Substring && Summary: Palindrome

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

Example 1:

Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.

Example 2:

Input: "cbbd"
Output: "bb"

Analysis: 想到了Naive的方法,时间复杂度为O(N^3),知道肯定不是最优的方法,一时也想不出别的方法,于是看了网上的做法,最后采纳了这个人的方法,感觉他讲的比较全面:http://www.programcreek.com/2013/12/leetcode-solution-of-longest-palindromic-substring-java/

1. Naive Approach:  The time complexity is O(n^3). If this is submitted to LeetCode onlinejudge, "Time Limit Exceeded".

2. Dynamic Programming: 

Let s be the input string, i and j are two indices of the string.

Define a 2-dimension array "table" and let table[i][j] denote whether substring from i to j is palindrome.

Start condition:

table[i][i] == 1;
table[i][i+1] == 1  => s.charAt(i) == s.charAt(i+1) 

Changing condition:

table[i][j] == 1 => table[i+1][j-1] == 1 && s.charAt(i) == s.charAt(j)

Time O(n^2) Space O(n^2)

以下代码参考了Code Ganker的,这种方法使用两层循环,时间复杂度是O(n^2)

 1 public String longestPalindrome(String s) {
 2     if(s == null || s.length()==0)
 3         return "";
 4     boolean[][] palin = new boolean[s.length()][s.length()];
 5     String res = "";
 6     int maxLen = 0;
 7     for(int i=s.length()-1;i>=0;i--)
 8     {
 9         for(int j=i;j<s.length();j++)
10         {
11             if(s.charAt(i)==s.charAt(j) && (j-i<=2 || palin[i+1][j-1]))
12             {
13                 palin[i][j] = true;
14                 if(maxLen<j-i+1)
15                 {
16                     maxLen=j-i+1;
17                     res = s.substring(i,j+1);
18                 }
19             }
20         }
21     }
22     return res;
23 }

2. Further Optimize:我自己的进一步考虑,空间可以进一步优化从O(n^2) -> O(n), 因为我们并不需要存储从任意点开始子字符串的回文情况,例如计算从i开始的回文,我们只需要从i+1开始的回文信息,而不需要从i+2,i+3,...等等节点开始的回文信息,因此只需要一个一位数组记录i+1开始的情况,这样二维数组变一维

3. 中心扩散法 Spread From Center

复杂度

时间 O(n^2) 空间 O(1)

思路

动态规划虽然优化了时间,但也浪费了空间。实际上我们并不需要一直存储所有子字符串的回文情况,我们需要知道的只是中心对称的较小一层是否是回文。所以如果我们从小到大连续以某点为个中心的所有子字符串进行计算,就能省略这个空间。 这种解法中,外层循环遍历的是子字符串的中心点,内层循环则是从中心扩散,一旦不是回文就不再计算其他以此为中心的较大的字符串。由于中心对称有两种情况,一是奇数个字母以某个字母对称,而是偶数个字母以两个字母中间为对称,所以我们要分别计算这两种对称情况。

 1 class Solution {
 2     int lo, maxLen;
 3     
 4     public String longestPalindrome(String s) {
 5         if (s == null || s.length() == 0) return "";
 6         
 7         for (int i = 0; i < s.length(); i ++) {
 8             findPalindrome(s, i, i);  //assume odd length, try to extend Palindrome as possible
 9             findPalindrome(s, i, i + 1);  //assume even length.
10         }
11         
12         return s.substring(lo, lo + maxLen);
13     }
14     
15     public void findPalindrome(String s, int l, int h) {
16         while (l >= 0 && h < s.length() && s.charAt(l) == s.charAt(h)) {
17             if (h - l + 1 > maxLen) {
18                 maxLen = h - l + 1;
19                 lo = l;
20             }
21             l --;
22             h ++;
23         }
24     }
25 }

O(N)的方法有Manacher's Algorithm, 比较复杂,有时间再去搞懂吧: https://www.felix021.com/blog/read.php?2040

原文地址:https://www.cnblogs.com/EdwardLiu/p/3792493.html