如何获取字符串中最长回文字子串

   在给定的字符串中获取其中最长的回文字子串,例如:在“kmoomq”中的是”moom“。

   思路1:首先要找到两边一样字符,再两边开始往中间判断,如上例中的”kmoomq“,从”k"开始,到”m"找到另一个"m",再判断中间是否符合。(不推荐,时间复杂度高)

  思路二:由中间对称点(遍历字符串)向两边判断,找到半径最长的,记录中心点和半径,以便可以截取最长回文子串。

      方法一:马拉力(Manacher‘s Algorithm)算法

           首先,对原始字符串(称为s)进行处理,即在开头插入”$"中间插入“#”结尾插入"@"。例如将上例处理为“$#k#m#o#o#m#q#@"(称为str),好处在于将字符串长度统一变为奇数,以及为下面创造一个与str等长的int型一维数组p[](p[i]等于以字符串str中i为中心点的回文字半径长度)提供两个关系式。

           (1) length = p[i] - 1       length:是字符串str下标为i的字符在字符串s中表示的回文字的长度。

           (2)radius = (i-p[i])/2      radius:是字符串str下标为i的字符在字符串s中表示的回文字的半径。

       

public class MyTest1 {

    public static String Manacher(String s){
       //空字符
      if(s==null||s.length==0){
          return "";
      }
      //处理传入字符串
      String str = "$#";
      for(int i=0;i i ? Math.min(p[2*id-i],mx-i):1;
         //这就是判断str字符串第i个位置以半径为p[i]的值进行逐渐加一增长比较
         while(str.charAt(i+p[i])==str.charAt(i-P[i])){
               p[i]++;
         }
         //更新mx和id的值
         if(p[i]+i>mx){
            mx = p[i]+i;
            id = i;
         }
         if(length

  在代码中的  p[i] = mx > i? Math.min(mx-i,p[2*id-i]):1  比较经典难以理解,其实就是去找找捷径,让p[i]的值不用从1开始加起。

       这是个三目运算符,就是给p[i]赋值,

       第一种情况:

              如果mx > i,假设j = p[2*id-i],就让p[i] 等于(mx - i)和j中更小的值。先来看看(mx - i)和j比较有啥意义。

               mx是在本次i之前的回文字子串所能到达的最右值 ,其中心点为id。那么在mx关于id的对称点到mx这个范围内有回文字的特性,即i关于id对称的点j(2*id=i+j  =>  j= 2*id - i)有p[i] = p[j] 的关系。

               当p[j]要小于mx - i时,即i+p[j]

如何获取字符串中最长回文字子串_第1张图片

               当p[j]要不小于mx - i时,因为有回文字的特性只在mx关于id的对称点到mx这个范围内。所以p[i]初始值赋值为mx-i

如何获取字符串中最长回文字子串_第2张图片

       第二种情况:

             如果mx < i,因为有回文字的特性只在mx关于id的对称点到mx这个范围内,所以只能乖乖给p[i]赋值为1,再开始判断是否累加。

    该算法时间复杂度度为线性。

   方法二:方法一的优化算法

       有了方法一的解释这个理解起来并不难,相比起来优点在于不用预先处理字符串。

public class MyTest2 {
    public static String Manacher(String s) {
        if(s==null||s.length()==0){
            return "";
        }
        int[] range = new int[2];
        char[] mychar = s.toCharArray();
        for (int i = 0; i < mychar.length; i++) {
            i = find(range,i,mychar);
        }
    return s.substring(range[0],range[1]+1);
    }
    public static int find(int[] range,int left,char[] mychar){
        int max = mychar.length-1;
        int right = left;

        while (right0&&right

 

你可能感兴趣的:(如何获取字符串中最长回文字子串)