最长回文子串的Manacher算法入门

版权声明:转载请注明原出处啦QAQ(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/86257621

一.最长回文子串问题与Manacher算法.

回文串,是一种满足一定条件的字符串,设字符串S的第i位为 S [ i ] S[i] (字符串从1开始),那么S为回文串仅当 S [ i ] = S [ S i + 1 ] \forall S[i]=S[|S|-i+1] .

一个串的最长回文子串定义为,这个串的一个最长的子串满足这个子串为回文串.

求最长回文子串的朴素算法是 O ( n 2 ) O(n^2) 的,但是Manacher算法可以在线性时间复杂度内求解一个串的最长回文子串.


二.Manacher算法流程.

我们在看Manacher算法前,先来看朴素算法.朴素算法的思路就是枚举对称位置,然后从对称位置开始向两边拓展直到失配,时间复杂度为 O ( n 2 ) O(n^2) .

但是我们发现这个朴素算法的对称中心有可能是一个字符,也有可能是两个字符中间的空隙,这样自己并不好处理.所以我们在所有字符的空隙间插入一个在原串中不会出现的字符,并在开头加上两个,末尾加上一个避免边界判定.

比如下图中第一个串处理后就变成了第二个串:
在这里插入图片描述
处理完之后,我们发现现在对称中心就只会是在字符上了.并且考虑一个在处理过的串上的一个回文子串 S [ l . . r ] S[l..r] ,设它的对称中心为字符 S [ m i d ] S[mid] ,那么在原串上对应的回文子串长度就是 S [ m i d . . r ] S[mid..r] 的长度减1.那么现在的问题就变成了求对于每个mid值,求以它为中心拓展的最长的 S [ m i d . . r ] S[mid..r] 长度,我们设这个值为 p a l [ m i d ] pal[mid] .

接下来的部分与扩展KMP比较像,如果有兴趣的可以去看看扩展KMP.
在这里插入图片描述
我们设绿色指标前的所有pal我们都已经处理出来了,红色线段是一个回文串,而且以红色指标为对称中心,且蓝色与绿色指标关于红色指标对称.通过回文串的定义,我们发现一个回文串翻转后仍然是回文串,所以蓝色指标的pal值小于或等于绿色指标的pal值.

到这里我们就可以设计出一个算法了.与扩展KMP类似的,我们记录一个当前右端点最右边的红色线段的对称中心p.然后在求解i的时候,我们找到它的对应点 p ( i p + 1 ) + 1 = 2 p i 1 p-(i-p+1)+1=2p-i-1 ,分成两种情况:
1. i > p + p a l [ p ] 1 i>p+pal[p]-1 ,也就是i在右端点最右的红色线段之外,这时我们发现没有任何信息可以使用,所以直接暴力拓展,并更新p.
2. i p + p a l [ p ] 1 i\leq p+pal[p]-1 ,这个时候我们有信息可以用,也就是先让 p a l [ i ] = p a l [ 2 p i 1 ] pal[i]=pal[2p-i-1] .如果还可以往右拓展就暴力拓展,并更新p.

注意到这个算法只有当一个点没有被拓展过,才会被拓展一次,所以时间复杂度是 O ( n ) O(n) 的.

代码如下:

int manacher(char *c,int len){
  int n,ans=0;
  tmp[1]='#';tmp[n=2]='#';
  for (int i=1;i<=len;++i)
    tmp[++n]=c[i],tmp[++n]='#';
  int p=0,t;
  for (int i=1;i<=n;++i){
    pal[i]=i<=p+pal[i]-1?pal[2*p-i+1]:1;
    while (tmp[i+pal[i]]==tmp[i-pal[i]]) ++pal[i];
    if (i+pal[i]>p+pal[p]) p=i;
    ans=max(ans,pal[i]-1);
  }
  return ans;
}



三.例题与代码.

题目:luogu3805.
代码如下(被卡常只有75分):

#include<bits/stdc++.h>
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=11000000;

char tmp[N*2+9];
int pal[N*2+9];

int manacher(char *c,int len){
  int n,ans=0;
  tmp[1]='#';tmp[n=2]='#';
  for (int i=1;i<=len;++i)
    tmp[++n]=c[i],tmp[++n]='#';
  int p=0,t;
  for (int i=1;i<=n;++i){
    pal[i]=i<=p+pal[i]-1?pal[2*p-i+1]:1;
    while (tmp[i+pal[i]]==tmp[i-pal[i]]) ++pal[i];
    if (i+pal[i]>p+pal[p]) p=i;
    ans=max(ans,pal[i]-1);
  }
  return ans;
}

char c[N+9];
int n;

Abigail into(){
  scanf("%s",c+1);
  n=strlen(c+1);
}

Abigail outo(){
  printf("%d\n",manacher(c,n));
}

int main(){
  into();
  outo();
  return 0; 
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/86257621