Manacher

Manacher算法

1.应用

给定一个字符串s,求s中最长回文子串的长度;(一系列与回文串有关的问题)

2.经典方法求回文子串

s a12b->str #a#1#2#b#
  注意点:不一定用#,其他任意字符均可,因为我们总是用虚的位置(填充字符匹配填充字符)匹配虚的位置的
对于str我们求出每个位置的回文长度数组pArr
  		#a#1#2#b#
index 012345678
pArr  131313131
结果为 max(pArr)/2
时间复杂度 O(n^2

3.概念引入

1.回文半径/直径
str  #a#1#2#b#,我们定义回文半径数组pArr
pArr 121212121 
直径  131313131
2.回文半径扩充过程的最右边界R,中心点C
str    #1#2#2#1#
index  012345678
当i=0时:[0,0],R=0C=0
当i=1时:[0,2],R=2C=1
当i=2时:[2,2],R=2C=2
当i=3时:[2,4],R=4C=3
当i=4时:[0,8],R=8C=4
...
可以看出CR是伴生关系,当R变化的时候C是一定会发生变化的

3.Manacher流程

为了方便理解,以下定义的变量

i:遍历到i的位置,以i位置为中心,求i的最大回文半径
R:之前的i在遍历过程中扩充到的最右的位置(初始值为-1L:之前的i在遍历过程中扩充到的最右的位置时的起始位置
C:之前的i在遍历过程中扩充到的最右的位置时的中心点的位置(初始值为-1)
i‘:C为中心做的i的对称位置

以上概念不理解没关系,我们通过流程来解释清楚

1.当i>=R时,暴力扩

str #1#2#1#  
    0123456
当i=0时,此时i>R,我们使用经典方法扩,发现走不下去(越界),更新R=0C=0
当i=1时,此时i>R,我们使用经典方法扩,发现走不下去(越界),更新R=2C=1
当i=2时,此时i==R,我们使用经典方法扩,发现走不下去(1=2

2.当i<R时

分析一般情况

str # 1 # 2 # 2 # 1 #
    L i'    C     i R
1.当i‘的区域在L…R回文区域里的时
str a b c b d k s k d b c b a(这里为了方便去掉#字符)
    L   i‘      C       i   R
  此时i的回文半径就等于i’的回文半径
证明
  	[...x(..?..)y...C..z(..?..)k...]//?即代表i的位置,()区域为对应的回文直径
  	L                C                R
  x!=y x=z y=k ->z!=k
2.当i‘的区域在L…R回文区域外的时
str (a b c d e d c b a) k a b c d e d c f t
         L   i'         C         i   R
  此时i的回文半径等于R-i
  证明
  	(...z[..i'..k..)..C....x(..i..)]y.//()为i‘扩充出来的区域,[]为此时最大R,C的区域
    k==x z==k 如果还能扩 x==y,如果x==y那么y==z,那就说明[]回文区域求错了
3.当区域压线时
str a b c d c b a k s k a b c d c b a ?
    L     i'        C         i     R
                        a b c d c b a这一块是不需要验的,从需验证k是否等于? 开始

3.代码实现

public class Manacher {
    public static char[] manacherString(String str){
        char[] charArr = str.toCharArray();
        char[] res=new char[charArr.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 maxLcpLength(String str){
        if(str==null || str.length()==0){
            return 0;
        }
        char[] charArr = manacherString(str);
        int[] pArr=new int[charArr.length];
        int R=-1;
        int C=-1;
        int res=1;
        for (int i = 0; i < pArr.length; i++) {
            //统一处理不需要配对的长度
            pArr[i]=R>i?Math.min(pArr[2*C-1],R-i):1;
            while(i+pArr[i]<charArr.length && i-pArr[i]>=0){
                if(charArr[i+pArr[i]]==charArr[i-pArr[i]]){
                    pArr[i]++;
                }else{
                    break;
                }
            }
            if(i+pArr[i]>R){
                R=i+pArr[i];
                C=i;
            }
            res=Math.max(res,pArr[i]);
        }
        return res-1;
    }
    public static void main(String[] args) {
        String res="abcdfrrfdcba";
        System.out.println(maxLcpLength(res));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值