您的位置:首页 > 其它

回文串算法Manacher

2017-12-26 20:31 99 查看

回文串算法Manacher

首先用一个非常巧妙的方式,将所有可能的奇数/偶数长度的回文子串都转换成了奇数长度:在每个字符的两边都插入一个特殊的符号。比如 abba 变成 #a#b#b#a#, aba变成 #a#b#a#。 为了进一步减少编码的复杂度,可以在字符串的开始加入另一个特殊字符,这样就不用特殊处理越界问题,比如$#a#b#a#。

下面以字符串12212321为例,经过上一步,变成了 S[] = “$#1#2#2#1#2#3#2#1#”;

然后用一个数组 P[i] 来记录以字符S[i]为中心的最长回文子串向左/右扩张的长度(包括S[i]),比如S和P的对应关系:

S # 1 # 2 # 2 # 1 # 2 # 3 # 2 # 1 #

P 1 2 1 2 5 2 1 4 1 2 1 6 1 2 1 2 1

(p.s. 可以看出,P[i]-1正好是原字符串中回文串的总长度)

下面计算P[i],该算法增加两个辅助变量id和mx,其中id表示最大回文子串中心的位置,mx则为id+P[id],也就是最大回文子串的边界。

这个算法的关键点就在这里了:

Mp[i] = mx > i ? min(Mp[2*id-i],mx-i):1;

其实就是对称点和边界的问题。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1000010;

char Ma[maxn*2];
int Mp[maxn*2];
char s[maxn];

void Manacher(char s[],int len)
{
int l = 0;
Ma[l++] = '$';
Ma[l++] = '#';
for(int i = 0;i < len; i++) {
Ma[l++] = s[i];
Ma[l++] = '#';
}
Ma[l] = 0;
int mx = 0,id = 0;
for(int i = 0;i < l; i++) {
Mp[i] = mx > i ? min(Mp[2*id-i],mx-i):1;
while(Ma[i+Mp[i]] == Ma[i-Mp[i]]) Mp[i]++;
if(i + Mp[i] > mx) {
mx = i + Mp[i];
id = i;
}
}
}
int main()
{
freopen("in.txt","r",stdin);
int ncase = 1;
while(scanf("%s",s) == 1) {
if(strcmp(s,"END") == 0) break;
int len = strlen(s);
Manacher(s,len);
int ans = 0;
for(int i = 0;i < 2*len+2; i++)
ans = max(ans,Mp[i]-1);
printf("Case %d: %d\n",ncase++,ans);
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Manacher