Manacher算法笔记 C++

Manacher算法简介:

1.做用:Manacher算法又名马拉车算法,用来求一个字符串中最长回文子串的长度。ios

2.复杂度分析:时间复杂度为O(n)。算法

算法核心思想:

1.伪代码:假设str为待判断的字符串,len[ i ]数组存放以该 str[ i ] 字符为中心的最长回文子串的长度,mid为当前最长回文子串的中点,mx为当前最长回文子串的右边界,那么对当前位置 i 有以下伪代码:(此时以知道前 i - 1个字符中最长回文子串的长度以及以每一个字符为中心的最长回文串的长度)数组

  1. 若 i < mx,则 len[ i ] = min ( len[ 2*mid - i ] , mx - i )。
  2. 不然len[ i ] = 1。
  3. 对 i 位置上的字符向两边进行匹配,更新len[ i ]的值,更新结束后更新mid和mx的值。

2.算法思路:为什么能按照这样步奏执行算法呢?spa

2.1 i < mx : code

首先,若是 i < mx,说明字符 str[ i ] 在以mid为中心的回文串中,那么其与以 str[ j ]( i 关于 mid 的对称位置)为中心的最长回文串有关。而以 str[ j ]字符为中心的最长回文串有三种可能:blog

  • 一种是该串(以j为中心的最长回文串)仍在以mid为中心的最长回文串中
  • 一种是超出
  • 最后一种是恰好相等

咱们能够证实第一种与第二种状况下,以 str[ i ] 为中心的最长回文串的长度为len[ j ] 或 mx - i。[ 注释1 ]而第三种状况下,以str[  i ] 为中心的回文串长度却可能大于len[ j ],故须要走伪代码中的第3步来更新len[ i ] 。字符串

2.2 i >= mx :string

而若是i >= mx 状况下,说明以 i 为中心的最长回文串没法从以前获得的len[ 1 ~ i -1]中得知,只能先令len[ i ] = 1,再分别向两边拓展,判断回文长度了,故走伪代码中二、3步。io

注释1:证实第1、第二中状况下以str[ i ]为中心的最长回文串的长度为len[ j ] 或 mx - i。模板

  • 若以str[ j ]为中心的最长回文串仍在以str[ mid ]为中心的回文串中,那么因为 i 与 j 关于mid对称,以下图,a != b,而d = a,c = b,故c != d,故len[ i ] = len[ j ]。

  • 若以str[ j ] 为中心的最长回文串超过了以str[ mid ]为中心的最长回文串,同理可证实以str[ i ] 为中心的回文串不可能与以str[ j] 为中心的最长回文串相等。

模板

//Manacher
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int MAXN = 1e5;
char str[MAXN];
char tmp[2*MAXN];
int len[2*MAXN];
int Manacher(char str[]){
	tmp[0] = '$';
	tmp[1] = '#';
	int str_len = strlen(str);
	for(int i = 1;i <= str_len;i++){
		tmp[2*i] = str[i-1];
		tmp[2*i+1] = '#';
	}
	tmp[2*str_len+2] = '\0';
	//cout << tmp << endl;
	int mx = 0;
	int maxlen = -1;
	int mid;
	for(int i = 1; tmp[i]; i++){
		if(i < mx) len[i] = min(len[2*mid-i],mx-i);
		else len[i] = 1;
		while(tmp[i-len[i]] == tmp[i+len[i]]) len[i]++;
		if(len[i]+i > mx){
			mx = len[i]+i;
			mid = i;
		}
		maxlen = max(maxlen,len[i]-1);
	}
	return maxlen;
}
int main(){
	scanf("%s",str);
	cout << Manacher(str);
	return 0;
}
相关文章
相关标签/搜索