manacher 算法详细介绍和证明

manacher 算法

定义

最大回文半径

一个长度为奇数的回文串以中心为起点,到它某一端点的连线,所经过的字符个数,包括两端。
例如:
s = a b c d a s = abcda s=abcda

c c c是回文串的中心,该字符串的最大回文半径是 3 3 3

m r , L : mr ,L: mrL:

mr 表示已经遍历的回文串的最靠右的端点,L表示最靠左的端点。
初始化为0。

m i d : mid : mid:

表示当前 m r mr mr所对应的文串的中心的位置的下标。

i : i : i:
表示当前遍历到的字符的位置。

i ’ : i’ : i:
表示当前遍历到的字符的位置关于 m i d mid mid对称的点, i ′ = m i d ∗ 2 − i i' = mid * 2 - i i=mid2i,由于是刚刚遍历到 i i i那么 i i i一定在 m i d mid mid的右边, i ′ i' i一定在 m i d mid mid的左边。

Manacher算法的作用

对于每一个字符串里面的字符,我们都能以线性的时间复杂度求出其最长回文半径。

Mancher算法的具体操作:

1.初始化:

首先,为了方便处理长度为偶数的字符串,我们把所有的偶字符串全部转化为长度为奇数的字符串。转化方式,对于原串的开头我们加上$结尾加上^,每个字符的两端我们加
#

例如,对于字符串:

abcba

abcba

转化后的字符串

$#a#b#c#b#a#^

初始化代码:

void init(char a[]){//把原始的串a转化为都是奇数长度的串b
	int k = 0;
	b[k ++] = '$',b[k ++ ] = '#';
	for(int i = 0; i < n; i ++ ){
		b[k ++ ] = a[i];
		b[k ++ ] = '#';
	}
	b[k ++ ] = '^';
	n = k;
}

然后从左往右遍历字符串,设当前位置是 i i i,讨论并更新 m r , m i d , i mr, mid, i mr,mid,i

  1. i < m i d < m r , L < i ’ − p [ i ′ ] < m i d i < mid < mr, L < i’ - p[i'] < mid i<mid<mr,L<ip[i]<mid

这时,根据对称性可知 p [ i ] = p [ i ′ ] p[i] = p[i'] p[i]=p[i]

  1. i < m i d < m r , i ′ − p [ i ′ ] < L i < mid < mr, i' - p[i'] < L i<mid<mr,ip[i]<L
    在这里插入图片描述
    这时
    p [ i ] = m r − i p[i] = mr - i p[i]=mri
    证明:假如 p [ i ] p[i] p[i]还能向右扩展,那么:
    在这里插入图片描述
    由对称性得:
    x x x y y y关于 i i i对称, 所以 x = y x = y x=y
    y y y a a a关于 m i d mid mid对称,所以 y = a y = a y=a,
    a a a b b b关于 i ′ i' i对称,所以 a = b a = b a=b
    所以 b = x b = x b=x。所以之前的 L , m r L,mr L,mr都可以向外继续扩展
    所以矛盾。
    3. i < m i d < m r , i ′ − p [ i ′ ] = = L i < mid < mr, i' - p [i'] == L i<mid<mr,ip[i]==L
    在这里插入图片描述
    这时,最小的 p [ i ] m i n = m r − i p[i]_{min} = mr - i p[i]min=mri

但是不知道后面的情况,所以 p [ i ] ⩾ p [ i ] m i n p[i] \geqslant p[i]_{min} p[i]p[i]min
我们需要暴力向 m r mr mr右边扩,知道找到 p [ i ] p[i] p[i]为止

4. i > m r i > mr i>mr
在这里插入图片描述

这时先设 p [ i ] = 1 p[i] = 1 p[i]=1,再暴力向外扩张。

代码

void manacher(char b[]){
	int mr = 0, mid;//mr不取到右端点,取的是开区间
	for(int i = 1; i < n; i ++ ){
		if(i < mr) p[i] = min(p[2 * mid - i], mr - i);
		else p[i] = 1;
		while(b[i - p[i]] == b[i + p[i]]) p[i] ++;
		if(i + p[i] > mr){
			mr = i + p[i];
			mid = i;
		}
	}
}

时间复杂度
O ( N ) O(N) O(N)
因为 p [ i ] p[i] p[i]如果变大,超过 m r mr mr m r mr mr也是一直向右更新,所以里面的 w h i l e while while执行的次数为 n n n次,所以时间复杂度是 O ( N ) O(N) O(N)的。

例题:
manacher算法
KFC Crazy Thursday

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值