Leetcode | 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.

Method I

之前先做了Palindrome Partitioning,所以一开始也用二维动态规划来做,vector<vector<int> > 报MLE。

然后又用和Minimum Window Substring同样的思路,在[0...i]找包含s[i]的最长回文串,最后再求最大。

在找包含s[i]的最长回文串时,首先保存了包含s[i-1]的最长回文串的长度pre,如果s[i]=s[i - pre - 1],那么从s[i-pre-1...i]是回文串,包含s[i]的最长回文串长度就是pre+2。

如果s[i]!=s[i - pre - 1],那么就从i - pre开始找包含s[i]的最长回文串。

最后取最长的回文串。算法复杂度O(n^2)。空间复杂度O(1)。

 1 class Solution {

 2 public:

 3     

 4     bool isPalindrome(string &str, int s, int e) {

 5         while (s <= e) {

 6             if (str[s] != str[e]) return false;

 7             s++;

 8             e--;

 9         }

10         return true;

11     }

12     

13     string longestPalindrome(string s) {

14         int n = s.length();

15         if (n <= 1) return s;

16         

17         int max = 1, start = 0, pre = 0;

18         for (int i = 1; i < n; ++i) {

19             int j = i - pre - 1;

20             if (s[i] == s[j]) {

21                 pre += 2;

22             } else {

23                 for (j++; j <= i; ++j) {

24                     if (isPalindrome(s, j, i)) {

25                         pre = i - j + 1;

26                         break;

27                     }

28                 }

29             }

30             if (pre > max) {

31                 max = pre;

32                 start = j;

33             }

34         }

35         return s.substr(start, max);

36     }

37 };

 Method II

以下摘自http://leetcode.com/2011/11/longest-palindromic-substring-part-i.html

二维dp。时间复杂度O(n^2)。空间复杂度O(n^2)。用table[1000][1000]就能通过,用vector<vector<int> > 报MLE,用vector<vector<bool> >报TLE。看来以后dp还是用数组来做快些。 

这里dp的构建过程和平常不一样,不是一个位置一个位置移动去构建,而是按长度来构建。len=1,2,....

 1 string longestPalindromeDP(string s) {

 2   int n = s.length();

 3   int longestBegin = 0;

 4   int maxLen = 1;

 5   bool table[1000][1000] = {false};

 6   for (int i = 0; i < n; i++) {

 7     table[i][i] = true;

 8   }

 9   for (int i = 0; i < n-1; i++) {

10     if (s[i] == s[i+1]) {

11       table[i][i+1] = true;

12       longestBegin = i;

13       maxLen = 2;

14     }

15   }

16   for (int len = 3; len <= n; len++) {

17     for (int i = 0; i < n-len+1; i++) {

18       int j = i+len-1;

19       if (s[i] == s[j] && table[i+1][j-1]) {

20         table[i][j] = true;

21         longestBegin = i;

22         maxLen = len;

23       }

24     }

25   }

26   return s.substr(longestBegin, maxLen);

27 }

Method III

从中心扩展的方法。时间复杂度O(n^2)。空间复杂度O(1)。

 1 string expandAroundCenter(string s, int c1, int c2) {

 2   int l = c1, r = c2;

 3   int n = s.length();

 4   while (l >= 0 && r <= n-1 && s[l] == s[r]) {

 5     l--;

 6     r++;

 7   }

 8   return s.substr(l+1, r-l-1);

 9 }

10  

11 string longestPalindromeSimple(string s) {

12   int n = s.length();

13   if (n == 0) return "";

14   string longest = s.substr(0, 1);  // a single char itself is a palindrome

15   for (int i = 0; i < n-1; i++) {

16     string p1 = expandAroundCenter(s, i, i);

17     if (p1.length() > longest.length())

18       longest = p1;

19  

20     string p2 = expandAroundCenter(s, i, i+1);

21     if (p2.length() > longest.length())

22       longest = p2;

23   }

24   return longest;

25 }

Method VI

 非常巧妙的方法。看了解释之后理解还是有些困难。

思路大概是这样子:

首先把原串S每两个字符之间插入一个#,存为T。这样做的好处是可以无差别地处理奇数和偶数的palindrome串。

如图,维护一个palindrome区间,[L,R],以C作为中心。可以看到数组p就是以每个位置为中心的palindrome串的长度。

i和i'关于C成镜像,也就是i'=C*2-i。

如果i>=R,P[i]=0.

否则,如果P[i']<=R-i,那么P[i]=P[i'],对称的;

如果P[i']>R-i,那么我们的是就要尝试扩展一下R;

如果成功扩展了,也就是i+P[i]>R,那么就移动中心C,C=i,扩展R=i+P[i];

最后再求一下P的最大值;

 1 // Transform S into T.

 2 // For example, S = "abba", T = "^#a#b#b#a#$".

 3 // ^ and $ signs are sentinels appended to each end to avoid bounds checking

 4 string preProcess(string s) {

 5   int n = s.length();

 6   if (n == 0) return "^$";

 7   string ret = "^";

 8   for (int i = 0; i < n; i++)

 9     ret += "#" + s.substr(i, 1);

10  

11   ret += "#$";

12   return ret;

13 }

14  

15 string longestPalindrome(string s) {

16   string T = preProcess(s);

17   int n = T.length();

18   int *P = new int[n];

19   int C = 0, R = 0;

20   for (int i = 1; i < n-1; i++) {

21     int i_mirror = 2*C-i; // equals to i' = C - (i-C)

22     

23     P[i] = (R > i) ? min(R-i, P[i_mirror]) : 0;

24     

25     // Attempt to expand palindrome centered at i

26     while (T[i + 1 + P[i]] == T[i - 1 - P[i]])

27       P[i]++;

28  

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  

37   // Find the maximum element in P.

38   int maxLen = 0;

39   int centerIndex = 0;

40   for (int i = 1; i < n-1; i++) {

41     if (P[i] > maxLen) {

42       maxLen = P[i];

43       centerIndex = i;

44     }

45   }

46   delete[] P;

47   

48   return s.substr((centerIndex - 1 - maxLen)/2, maxLen);

49 }

Line 26-27内循环,可以看作是扩展R的步骤,R在整个代码中是只增不减的,总的最多扩展n步,所以整个算法是2n的开销,时间复杂度为O(n),空间复杂度也是O(n)。

事后诸葛

对于这类,要求某个最优区间的。可以考虑:

1. 和Minimum Window Substring同样的思路,在[0...i]找包含s[i]的最优区间。然后再取最优;

2. 也可以尝试Method II,二维动态规划,构建的时候以区间长度来构建。

你可能感兴趣的:(substring)