2020.01.15日常总结

m a n a c h e r \color{green}{manacher算法}

\color{blue}{【例题】:} 给出一个只由小写英文字符a,b,c...y,z组成的字符串 S S ,求 S S 中最长回文串的长度(见洛谷P3805)。

\color{blue}{【思路】:} 我们先考虑暴力算法:枚举一个回文中心,然后向两边扩展。

这样做有两个问题:一是需要区分奇数和偶数。比如abaabba,我们肉眼一看就知道两个都是回文,但是对于偶数回文串而言,它们的回文中心不是一个可见字符(比如abba的回文中心在两个b之间)。二是时间复杂度太高。枚举的时间复杂度为 O ( S ) O(|S|) S |S| 表示字符串 S S 的长度)。而拓展的时间复杂度最坏为 O ( S ) O(|S|) ,总的时间复杂度为 O ( S 2 ) O(|S|^2)

m a n a c h e r \color{red}{manacher算法(中文名:马拉车算法)} ,是解决此问题的 O ( S ) O(|S|) 算法。首先,为了统一奇偶情况,我们在每个字符之间加上一个没有在 S S 中出现的字符(比如aba变成$a$b$a$)。

p [ i ] p[i] 表示以第 i i 个字符为回文中心的最长回文半径 r r 为最长回文右边界, m i d mid 为最长回文右边界中心, i i 表示当前求解 p [ i ] p[i]

考虑如何求 p p 数组,求解有好几种情况:

  • i i r r 左边且在 m i d mid 右边时, i i 关于 m i d mid 的对称点为 m i d × 2 i mid \times 2-i ,我们令 p [ i ] = p [ j ] p[i]=p[j] ,然后从 i + p [ i ] i+p[i] 开始拓展求解。
  • i i r r 右边时,我们就按原始方法进行计算。

\color{blue}{【代码】:}

const int N=11001000;
char s[N],s1[N<<1];
int p[N<<1],ans,n;
inline void manacher(){
	s1[0]='~';s1[1]='$';
	for(int i=1;i<=n;i++){
		s1[i*2]=s[i];
		s1[i*2+1]='$';
	}
}
inline void calc_answer(){
	for(int t=1,r=0,mid=0;t<=2*n+1;t++){
		if (t<=r) p[t]=min(p[mid*2-t],r-t+1);
		while (s1[t-p[t]]==s1[t+p[t]]) ++p[t];
		if (p[t]+t>r) r=p[t]+t-1,mid=t;
		if (p[t]>ans) ans=p[t];
	}
}
int main(){
//	freopen("t1.in","r",stdin);
	scanf("%s",s+1);
	n=strlen(s+1);
	manacher();
	calc_answer();
	printf("%d",ans-1);
	return 0;
}
发布了82 篇原创文章 · 获赞 4 · 访问量 1763

猜你喜欢

转载自blog.csdn.net/ZHUYINGYE_123456/article/details/103995891