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

        

  • 相关阅读:
    Java实现 LeetCode 27 移除元素
    Java实现 LeetCode 26 删除排序数组中的重复项
    Java实现 LeetCode 26 删除排序数组中的重复项
    Java实现 LeetCode 26 删除排序数组中的重复项
    Java实现 LeetCode 25 K个一组翻转链表
    Java实现 LeetCode 25 K个一组翻转链表
    Java实现 LeetCode 25 K个一组翻转链表
    Java实现 LeetCode 24 两两交换链表中的节点
    Java实现 LeetCode 24 两两交换链表中的节点
    Java实现 LeetCode 24 两两交换链表中的节点
  • 原文地址:https://www.cnblogs.com/hf-cherish/p/4572740.html
Copyright © 2011-2022 走看看