HF_Cherish

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

1. Question

求最长回文子串

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.

2. Solution

2.1 O(n3)

对每个字符,考察该字符(作为首字符)与其余字符(作为尾字符)组成的子串是否是回文串,找到最长的。判断是否是回文串耗时O(n),因此时间复杂度O(n3)

2.2 O(n2)

2.2.1 中心向外扩充法

对每个字符,将其作为回文串中心字符(可通过插入辅助字符使得仅考虑奇数回文串情况,eg. abcd--->#a#b#c#d#),考虑其最长回文串长度。此时,为每个中心字符找回文串用时O(n),因此时间复杂度O(n2)

 1 public class Solution {
 2     private int maxStart;
 3     private int maxEnd;
 4     
 5     private void expand( String s, int from, int end ){
 6         for( ; from>=0 && end<s.length() && (s.charAt(from) == s.charAt(end) ); from--,end++ );
 7         end--;
 8         from++;
 9         if( end-from > maxEnd-maxStart ){
10             maxStart = from;
11             maxEnd = end;
12         }
13     }
14     
15     //expand from a middle point, O(n2) time
16     public String longestPalindrome( String s ){
17         maxStart = 0;
18         maxEnd = 0;
19         for( int i=0; i<s.length()-1; i++ ){
20             expand( s,i,i ); //the result length is odd
21             expand( s,i,i+1 ); //the result length is even
22         }
23         return s.substring(maxStart, maxEnd+1);
24     }
25 }
expandFromMiddle

 

2.2.2 按长度依次扩充

人在找回文串时,一般都是先看到较短的回文串,然后再慢慢往外扩(类似上述中心向外扩充),即先找长度为2的回文串,再找长度为3的,为4的...长度长的可以复用长度短的信息。

由此,定义二维数组a[][]存储回文串信息,a[i][j]表示字符i到j是否是回文串,则有:

  a[i][i] = true;

  a[i][i+1] = ( chars[i] == chars[i+1] );

  a[i][j] = ( chars[i] == chars[j] ) && a[i+1][j-1];

 1 public class Solution {
 2     //DP, O(n2) time
 3     public String longestPalindrome( String s ){
 4         if( s.length()==1 )    return s;
 5         if( s.length() == 2 ){
 6             if( s.charAt(0) == s.charAt(1) )
 7                 return s;
 8             return s.substring(0, 1);
 9         }
10         //s.length() >2
11         boolean[][] p = new boolean[s.length()][s.length()];
12         int i;
13         for( i=0; i<s.length()-1; i++ ){
14             p[i][i] = true;
15             p[i][i+1] = ( s.charAt(i) == s.charAt(i+1) );
16         }
17         p[i][i] = true;        
18         for( i=2; i<s.length(); i++ )
19             for( int j=0, k=i; k<s.length(); j++, k++ )
20                 p[j][k] = ( s.charAt(j) == s.charAt(k) ) && p[j+1][k-1];
21         
22         int maxStart = 0;
23         int maxEnd = 0;
24         for( i=0; i<s.length(); i++ )
25             for( int j=s.length()-1; j>=i; j-- ){
26                 if( p[i][j] ){
27                     if( j-i > maxEnd-maxStart ){
28                         maxStart = i;
29                         maxEnd = j;
30                     }
31                     break;
32                 }
33             }
34         return s.substring(maxStart, maxEnd+1);
35     }
36 }
expandLength

2.3 O(n)

 考虑中心向外扩充,它遍历每个字符为其找回文串。而事实上,由于回文串的对称性,中心点左侧的回文性和右侧字符的部分回文性是一致的,这部分数据可以重用,而不用重复计算。

改进上述算法,重新定义下列变量:

  • r[]:存放以每个点为中心点的最长回文串的半径(以便复用该信息)
  • max:存放目前回文串走到最远的中心字符索引,即以max为中心的回文串,其回文串最后一个字符是目前走的最远的。下面简称max为最远覆盖点,回文串范围为覆盖范围
  • i:定义当前访问的字符索引,要为字符i计算其回文串长度
    • 如果i在max的覆盖范围,考察i的对称点的覆盖范围
      • 如果对称点覆盖边界在max覆盖边界之内,则无需对i再扩展,r[i] = r[i的对称点]
      • 如果对称点覆盖边界与max某覆盖边界重合,则对于i,最远覆盖边界之外的点是下一个可能在i的回文串范围的字符,应当从最远覆盖边界开始继续向外扩充,计算r[i]
    • 如果不在,以i为中心点向外扩充(此时没有可利用的信息)

利用渐进计算分析可知,该算法时间复杂度O(n)

 1 public class Solution {
 2     
 3     //adding extra character to make all substring as an odd number string. such as: abcd--->#a#b#c#d#
 4     private String preProcess( String s ){
 5         StringBuilder res = new StringBuilder();
 6         for( int i=0; i<s.length(); i++ ){
 7             res.append('#');
 8             res.append( s.charAt(i) );
 9         }
10         res.append( '#' );
11         return res.toString();
12     }
13     
14     //expand a palindrome, return the expanding length
15     private int expand( String s, int start, int end ){
16         int newS = start;
17         int newE = end;
18         for( ; newS >= 0 && newE < s.length() && s.charAt(newS) == s.charAt(newE); newS--, newE++ );
19         return start - newS;
20     }
21     
22     //O(n) time, using symmetry feature, calculate the longest palindromic centered by each point
23     public String longestPalindrome( String s ){
24         String newS = preProcess(s);
25         
26         int[] r = new int[newS.length()];
27         r[0] = 0;
28         int max = 0;
29         
30         for( int i=1; i<newS.length(); i++ ){
31             int j = r[max] + max;   //present farthest palindrome range, inclusive
32             if( j >= newS.length() )
33                 break;
34             //if i is in the range, it's possible to fast calculate the palindromic length for point i
35             if( i<=j ){
36                 int symI = r[2*max-i];
37                 int remainRange = j - i;
38                 if( symI < remainRange )
39                     r[i] = symI;
40                 else
41                     r[i] = expand( newS, i-remainRange-1, j+1 ) + remainRange;
42             }
43             //if i is not int the range, r[i] = expand(i)
44             else
45                 r[i] = expand( newS, i-1, i+1 );
46             
47             if( r[i] + i > j )  max = i;
48         }
49         max = 0;
50         for( int i=0; i<r.length; i++ )
51             if( r[i] > r[max] ) max = i;
52         return s.substring( (max-r[max]+1)/2, (max+r[max])/2 );
53     }
54 }
betterExpandFromMiddle

 

    

posted on 2015-06-12 22:35  HF_Cherish  阅读(168)  评论(0编辑  收藏  举报