zoukankan      html  css  js  c++  java
  • Longest Palindromic Substring

    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

        

  • 相关阅读:
    SNOI 2019 字符串
    1068: [SCOI2007]压缩
    POJ 1848 Tree 树形DP
    BZOJ bzoj1396 识别子串
    BZOJ 4503: 两个串
    BZOJ 2302: [HAOI2011]Problem c(数学+DP)
    BZOJ 3157: 国王奇遇记 (数学)
    CF_528D
    BZOJ 3000: Big Number (数学)
    新の开始
  • 原文地址:https://www.cnblogs.com/hf-cherish/p/4572740.html
Copyright © 2011-2022 走看看