20201011day32 复习5:Manacher、KMP

KMP算法

回文串问题

查找一个字符串中的最长回文子串

暴力算法

对于每个字符串,枚举每个位置并向两边扩展,看是否回文。

缺点:1.无法处理偶数长度串 2.平均时间复杂度\(O(n^2)\)

manacher算法

  1. manacher对字符串的预处理
    对于偶数长度字符串的预处理,使得它变成奇数长度字符串,即在字符串的首位加上一个特定字符,如aa变成#a#a#,这使得原本长度为\(n\)的字符串变为\(2n+1\),保证是奇数。
    这里解释一下为什么预处理后不会影响对字符串的扩展匹配。比如我们的原字符串是 a,假设预处理后的字符串是 #a#a#,我们在任意一个点,比如字符#,向两端匹配只会出现a 匹配 a# 匹配 # 的情况,不会出现原字符串字符与特殊字符匹配的情况,这样就能保证我们不会改变原字符串的匹配规则。通过这个例子,你也可以发现实际得到的结果与上述符合。

补充:首尾可以放置不同的字符,如^&,这样可以自动判断跳出循环,而不用担心越界
2. 说明
(1)对于一个回文串,有且仅有一个对称中心,叫做回文对称中心。
(2)在一个回文串内,任选一段区间\(X\),一定存在关于“回文对称中心”对称的一个区间,把这个区间叫做关于区间\(X\)的对称区间。区间和对称区间是全等的。
(3)如果一个区间的对称区间是回文串,那么这个区间也一定是一个回文串。在大的回文串内,它们的回文串半径相等。
(4)我们通过确定关系预先得到的回文半径,它的数值必定小于等于这个位置真实的回文串半径。

因此,我们如果可以记录以每个位置为中心的回文串半径,当我们通过另一个回文中心将这个原先的中心点对称过去的时候,就可以确定对称过去的那个点的回文半径了。

考虑另一个回文中心如何确定,就是那个极大回文串的回文中心,也就是边界顶着右边我们已知的最远位置的,最长的回文串。

然而考虑到,我们只能确认我们已知的回文串内的对称关系和回文半径等量关系,关于这个极大回文串右侧,我们啥也不知道。

记录这些数据到p数组,同时记录一个mid,一个r,分别代表已经确定的右侧最靠右的回文串的对称中心和右边界。

那么我们扫描到一个新的字符时候,怎么先确定它的部分回文半径呢?若当前扫描到的位置为i,\(mid\le i\le r\),我们就可以找到他的一个对称点,位置是\(2\times mid-i\)

所以,拓展一个新点时,我们不必从这个点左右两边第一个位置开始向两边拓展,可以预先确定一部分回文串。所以可以看出字符串的线性复杂度。

  1. 若扩展一个新的关于该字符的回文半径,可以先确定一部分p[i]。
  2. 我们知道我们能确定的范围,其右侧不得大于r,即\(p_i+i-1\le r\),移项得\(p_i\le r-i+1\)

取一个min,所以p[i]=min(p[mid*2-i],r-i+1);,最终答案是max(p[i])-1

code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=11000002;
char data[maxn<<1];
int p[maxn<<1],cnt,ans;
inline void qr(){
      char c=getchar();
      data[0]='~',data[cnt=1]='|';
      while(c<'a'||c>'z') c=getchar();
      while(c>='a'&&c<='z') data[++cnt]=c,data[++cnt]='|',c=getchar();
}
int main(){
      qr();//优化后的读入
      for(int t=1,r=0,mid=0;t<=cnt;++t){
	    if(t<=r) p[t]=min(p[(mid<<1)-t],r-t+1);
	    while(data[t-p[t]]==data[t+p[t]]) ++p[t];
	    //暴力拓展左右两侧,当t-p[t]==0时,由于data[0]是'~',自动停止。故不会下标溢出。
	    //假若我这里成功暴力扩展了,就意味着到时候r会单调递增,所以manacher是线性的。
	    if(p[t]+t>r) r=p[t]+t-1,mid=t;
	    //更新mid和r,保持r是最右的才能保证我们提前确定的部分回文半径尽量多。
	    if(p[t]>ans) ans=p[t];
      }
      printf("%d\n",ans-1);
      return 0;
}

posted @ 2020-10-11 17:11  刘子闻  阅读(67)  评论(0编辑  收藏  举报