「manacher」

前言

发现大神指$skyh$都用$manacher$水回文自动机的题,就学了学这个做法。

应用

  • 求以某个点为中心的最长回文半径
  • $O(n)$
  • 由第一点可以拓展求以某个点为终点/起点的最长回文
  • 没了(怎么感觉跟回文自动机差不多的说)

代码

#include<bits/stdc++.h>
using namespace std;
const int N=3e7;
int n,r[N];
char str[N],s[N];
int main(){
    scanf("%s",str+1);int l=strlen(str+1);
    s[0]='/'; s[++n]='%';
    for(int i=1;i<=l;++i) s[++n]=str[i],s[++n]='%';
    int ans=-0x3f3f3f3f;
    for(int i=1,maxr=0,thatmid=0;i<=n;++i){
        int j=2*thatmid-i;
        if(i<=maxr) r[i]=min(r[j],maxr-i+1);
        while(s[i+r[i]]==s[i-r[i]]) ++r[i];
        if(r[i]+i-1>maxr) maxr=i+r[i]-1,thatmid=i;
        ans=max(ans,r[i]);
    }
    printf("%d\n",ans-1);
}

例题

P1659 [国家集训队]拉拉队排练

求前k长回文。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e6+50,mod=19930726;
ll n,k,r[N],len[N];
char s[N],str[N];
ll mgml(ll a,ll b,ll ans=1){
    for(;b;b>>=1,a=a*a%mod) if(b&1) ans=ans*a%mod;
    return ans;
}
int main(){
    scanf("%lld%lld%s",&n,&k,str+1); int l=strlen(str+1);
    s[n=0]='/';s[++n]='%';
    for(int i=1;i<=l;++i) s[++n]=str[i],s[++n]='%';
    for(ll i=1,maxr=0,thatmid=0;i<=n;++i){
        int j=thatmid*2-i;
        if(i<=maxr) r[i]=min(maxr-i+1,r[j]);
        while(s[i+r[i]]==s[i-r[i]]) ++r[i];
        if(i+r[i]-1>=maxr) maxr=i+r[i]-1,thatmid=i;
        if(i&1ll^1ll) len[r[i]-1]++;
    }
    int st=(n&1ll)?n:n-1;
    ll ans=1;
    for(int i=st;i>=1;i-=2){
        len[i]+=len[i+2];
        ll Lim=min(len[i],k);
        ans=ans*mgml(i,Lim)%mod;
        k-=Lim;
        if(!k) break;
    }
    printf("%lld\n",ans);
    return 0;
}
View Code

P4555 [国家集训队]最长双回文串

求以某个点为起点/终点的最长回文。

还是上模板,然后因为要保证两个回文拼接起来不能交叉,于是就枚举'%'作为分界点统计答案。

再计算以某个点为起点/终点的最长回文有个方法,在$manacher$中只把饱和回文加进数组,最后再$O(n)$扫数组向一边更新答案,因为要两个回文拼接起来,因此一个回文的不算。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=4e5+50;
int n,r[N],L[N],R[N];
char s[N],str[N];
signed main(){
    scanf("%s",str+1); int l=strlen(str+1);
    s[n=0]='/';s[++n]='%';
    for(int i=1;i<=l;++i) s[++n]=str[i],s[++n]='%';
    for(int i=1,maxr=0,thatmid=0;i<=n;++i){
        int j=thatmid*2-i;
        if(i<=maxr) r[i]=min(maxr-i+1,r[j]);
        while(s[i+r[i]]==s[i-r[i]]) ++r[i];
        if(i+r[i]-1>=maxr) maxr=i+r[i]-1,thatmid=i;
        L[i-r[i]+1]=max(L[i-r[i]+1],r[i]-1);
        R[i+r[i]-1]=max(R[i+r[i]-1],r[i]-1);
//        printf("%d %d %d %d %d\n",i-r[i]+1,L[i-r[i]+1],i+r[i]-1,R[i+r[i]-1],r[i]-1);
    }
    for(int i=1;i<=n;i+=2) L[i]=max(L[i],L[i-2]-2);
    for(int i=n;i>=1;i-=2) R[i]=max(R[i],R[i+2]-2);
    long long ans=0;
    for(int i=1;i<=n;i+=2) if(L[i]&&R[i]) ans=max(ans,(long long)L[i]+R[i]);
    printf("%lld\n",ans);
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/hzoi2018-xuefeng/p/12103744.html