Manacher算法

 

Manacher算法: 在一个字符串中找到最长的回文字串
 
例如:abc12321def  最长回文字串为12321
传统思路是遍历每个字符,以此字符为中心向两边扩,看两边的字符是否相等,如果是回文串的长度是奇数的话,可以往两边扩,如果是偶数的话(例如1221)则不行!因此需要加特殊字符,例如(1221—>改为#1#2#2#1#)而且传统思路的时间复杂度比较高。如原始数据为N,加上特殊字符后字符串长度为2N,最差情况每个字符都相等,因此每个字符都扩一次,时间复杂度为O(N*2)
而manacher算法将时间复杂度优化到O(N)

 

 

 

如何做:
第一:同样需要将字符串加上特殊字符
第二:回文半径(abcba的回文半径为3(加上中心点))

          回文半径数组  dp[...],,,,,dp[i]表示第i个字符的回文半径 

第三:最右回文边界    例如#1#2#3#2#1#
                      下标C          012345678910
                      边界R          0  2  4          10  //下标为0是最右回文边界为0,下标为2时最右回文边界为4
为什么manacher算法比传统方法快,因为在找回文串的时候做了加速,,,

 

     

 

 

 

 

例如:a  b  c  d  e  d  c  b  a
                  i'      C      i       R       //C为中心  ,,,R为最右回文边界 ,找i的回文半径时,因为i'和i对称,因此i的回文半径
可能等于i'的回文半径,,,因此有两种大情况,三种小情况

 

 

(1)最右回文边界没有括住i的字符(暴力扩)
(2)i在R里面

 

 

   1.   i的回文半径一定在L~R内部,,,(L~R)为最右回文半径中心的回文直径  ,,,如果i'的回文半径小在L~R内,则i也在L~R内
    2.     i'的回文区域没完全在L~R内
            例如      ab  cdcbatttabcdc  k  
                               L    i'   C        i   R
                  i'的回文半径没完全在L~R中  设i'的L和R为a,d  ,,,d关于C的对称点为c,,,b为最右回文边界下一位字符
                  则a!=b           a==d            d==c          所以b!=c      则i的回文半径等于(R-i)
     3. a b c b a t  t  t a b c b a ?(t||k)
               i'      x  C          i                   如果i'的回文半径压线,则如果R后一位等于x位的则能扩,不能则不能扩(如等于t能扩,等于k不能扩)
 
时间复杂度    :(1)i不在R内暴力扩
                        (2)1.   i'回文半径在L~R内   i则知道 O(1)
                        (3)2.   i'回文半径部分在外   i则知道 O(1)
                            3.   i'回文半径压线          判断扩不扩   //(1)和(3).3都扩的话最多扩N个,则时间复杂度为O(N)

 

 

代码:
public class Manacher {
 public static char[] manacherString(String str) {
  char[] charArr = str.toCharArray();
  char[] res = new char[str.length() * 2 + 1];
  int index = 0;
  for (int i = 0; i != res.length; i++) {
   res[i] = (i & 1) == 0 ? '#' : charArr[index++];
  }
  return res;
 }
 public static int maxLcpsLength(String str) {
  if (str == null || str.length() == 0) {
   return 0;
  }
  char[] charArr = manacherString(str);
  int[] pArr = new int[charArr.length];
  int C = -1;   //回文半径的中心
  int R = -1;   //最右回文边界
  int max = Integer.MIN_VALUE;
  for (int i = 0; i != charArr.length; i++) {
   pArr[i] = R > i ? Math.min(pArr[2 * C - i], R - i) : 1;//如果i在最右回文边界右边暴力扩,否则基础回文半径为(R-i)
   while (i + pArr[i] < charArr.length && i - pArr[i] > -1) {
    if (charArr[i + pArr[i]] == charArr[i - pArr[i]])
     pArr[i]++;
    else {
     break;
    }
   }
   if (i + pArr[i] > R) {
    R = i + pArr[i];
    C = i;
   }
   max = Math.max(max, pArr[i]);     //#a#b#a#
  }
  return max - 1;
 }
 
 public static void main(String[] args) {
  String str1 = "abc1234321ab";
  System.out.println(maxLcpsLength(str1));
 }
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值