[leetcode]Longest Palindromic Substring

Longest Palindromic Substring

题意:找出一个字符串中最长的回文子串

解法:

         O(N^2):DP解法:

                            以f[i][j]代表字符串第i到第j个字符组成的子串是否是回文。

                            若a[i]==a[j],则f[i][j]=f[i+1][j-1],否则f[i][j]=false

                            最外层循环枚举子串的长度

                            时刻记录最长的回文子串的长度,若在循环某个长度i时,发现最长的回文子串长度<i-2,则这一层不可能有回文子串,程序结束。

public class Solution147 {

   public String longestPalindrome(String s) {

       if (s.length()==0){

                return s;

       }

       if (s.length()==1){

                return s;

       }

       if (s.length()==2){

                if (s.charAt(0)==s.charAt(1)){

                          return s;

                }else{

                          returns.substring(1);

                }

       }

       int n=s.length();

       boolean[][] f=new boolean[n][n];

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

                f[i][i]=true;

       }

       int maxLength=1;

       String ans="";

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

                if(s.charAt(i)==s.charAt(i+1)){

                          f[i][i+1]=true;

                          if(2>maxLength){

                                   maxLength=2;

                                   ans=s.substring(i,i+2);

                          }

                }

       }

       for (int i=3;i<=n;i++){

                if (maxLength<i-2){

                          break;

                }

                for (intj=0;j+i-1<=n-1;j++){

                          if(s.charAt(j)==s.charAt(j+i-1)){

                                   f[j][j+i-1]=f[j+1][j+i-2];

                                   if(f[j][j+i-1]){

                                            if(i>maxLength){

                                                maxLength=i;

                                                ans=s.substring(j,j+i);

                                       }       

                                   }                                  

                          }else{

                                   f[j][j+i-1]=false;

                          }

                }

       }

       return ans;

    }

}

         O(N):参考http://blog.csdn.net/hopeztm/article/details/7932245

                   原文写得还算清楚,这里我用我的理解解释一遍这个叫Manacher’s Algorithm的算法。下面前两张图为引用

       算法第一步:将原来字符串XXXXX扩充成#X#X#X#X#X#,就是在所有字符之间加一个#,字符串前后各加一个#。在原来字符串中的每一种回文可能,在新串中都有一种对应,奇数长度的回文串,长度d,表示为以某非#字符为中心的,左右各向外扩展d的新串。偶数长度的回文串,长度d,表示为以某#字符为中心的,左右各向外扩展d的新串。举例,原来abcba,新串的某一段一定为#a#b#c#b#a#,以c为中心,左右各延展5个长度。再举例,原来abba,新串的某一段一定为#a#b#b#a#,以#为中心,左右各延展4个长度。我们以数组p来记录新串中每一个字符可以延展的最长长度。那么p最后的最大值,即为原串的最长回文的长度。

在计算p时,最直观暴力的方法是,对新串中的每一个字符,向外扩展,直到扩展不了。这个过程和在原串中的暴力解法是一样的。但我们观察如下某个情况,会发现在所有p的计算中,存在曾经的p值可以被后来p值所利用情况。看下图

         图中i为我们当前要计算的某个字符的位置,如果我们已经某个较长的回文中心所在的位置,例如C,p[C]=11,意味着从C位置-11到C位置+11,是个回文。而i关于回文中心C的镜像位置I’的p值为1,那么意味着p[i]一定为1,因为i和I’同样位于整个大回文串中,I’所能达成最长回文被包含在回文串中,i的情况一定与I’完全镜像。

但我们所说的前提I’的最长回文被包含在最长回文里,如果不完全包括的话,看下图。

         当计算i=15时,I’的p值为7,意味着以I’为中心的最长回文有一部分超出在目前大回文的范围,那么这一部分首先被我们舍弃,但大回文的右边界20与i=15的差的部分,很明显是以i为回文中心的右侧。也就是说当I’的值大于这个R-i时,我们就认为I’的值为R-i好了,这样一定不会出错。但究竟大于20的部分,会不会也是以i为中心的回文的一部分呢,我们继续循环判断就好了。

         说到这里,将会产生疑问,那么这个大的回文串(图中以C为中心,位置2到位置20)该如何维护呢,我们在从左到右求p[i]的过程中,该选取i左边的哪个大回文串呢?

首先,大回文串一定要包含i,否则计算p[i]一定用不上它的镜像信息。

那是否意味着,要维护左边所有的大回文串?

         这里原算法给出非常精妙的选择方法,全局只维护这样一个大回文串,每当算出一个i的回文串时,若i的最长回文串的右边界大于目前大回文串的右边界,就舍弃原来的大回文串,记录以i为中心,向左向右各为p[i]的回文串为新的大回文串。

这里给出一张我画的图。

         I代表要计算p值的点,目前已经得到的所有可以利用到大回文串,我们只用两个来说明,一个是以A为中心,从A1到A2的回文串,一个是以B为中心,从B1到B2的回文串。在计算p[i]时,从A1-A2可以利用的信息最多是从i到A2,从B1-B2可以利用的信息最多是从i到B2,明显的包含关系,说明以前所有的大回文串中,只需要记录右边界最靠右的就可以了,而与这个回文串的总长度无关。如果说能从A1-A2中得到i-A2这段信息,那么从B1-B2可以一定可以获得。

         算法的时间复杂度分析:我们的目的是计算p[i],而在计算个别p[i]时,存在需要向右拓展来判断以i为中心的回文是否延伸到目前大回文串的外面。而这个延伸过程,每向外延伸一个距离,最终都会使目前维护的大回文串的右边界向右拓展一步,而大回文串的右侧最多可以延展n次,所以我们对所有i的延伸判断最多有n次,综上所述,这个算法的时间复杂度为Θ(N)。

         另外,本题要求返回的是原来字符串的最长回文子串,只要将新串得到的最大p值和对应index通过简单计算即可得到原来字符串对应最长回文子串的初始位置了。

 

public class Solution147second {

         publicString longestPalindrome(String s) {

                   if(s.length()==0){

                            return"";

                   }

                   if(s.length()==1){

                            returns;

                   }

                   char[]sTemp=new char[s.length()*2+1];

                   sTemp[0]='#';

                   inthigh=1;

                   for(int i=0;i<s.length();i++){

                            sTemp[high]=s.charAt(i);

                            high++;

                            sTemp[high]='#';

                            high++;

                   }                

                   int[]p=new int[s.length()*2+1];

                   intc=0;

                   intr=0;

                   for(int i=1;i<s.length()*2+1;i++){

                            intiMirror=c-(i-c);

                            if(r>i){

                                     p[i]=Math.min(r-i,p[iMirror]);

                            }else{

                                     p[i]=0;

                            }

                            while((i-p[i]-1>=0)&&(i+p[i]+1<=s.length()*2)&&sTemp[i-p[i]-1]==sTemp[i+p[i]+1]){

                                     p[i]++;

                            }

                            if(i+p[i]>r){

                                     c=i;

                                     r=i+p[i];

                            }

                   }

                   intmax=0;

                   intindex=-1;

                   for(int i=0;i<2*s.length()+1;i++){

                            if(p[i]>max){

                                     max=p[i];

                                     index=i;

                            }

                   }

                   index=index/2-max/2;

                   returns.substring(index, index+max);

         }

}


以下可能是一种错误的算法: 

另一种O(N^2)的解法:

         同样参考http://blog.csdn.net/hopeztm/article/details/7932245

         KMP匹配,由于复杂度并没有比DP强,所以不做过多介绍,况且实现KMP也是很麻烦的事情。

Ps:按照原文算法,abcdba和abdcba进行匹配时,可以匹配到ab。但原String根本没有回文。所以我认为该算法是不正确的。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值