[专题总结]回文自动机

Posted hzoi-yzh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[专题总结]回文自动机相关的知识,希望对你有一定的参考价值。

模板

#include<cstdio>
#include<cstring>
#include
#define reg register
#define F(i,a,b) for(register int (i)=(a);(i)<=(b);++(i))
using namespace std;
const int N=100005;
int len,n;
char s[N];
struct PAM{
    int las,num;
    int len[N],son[N][26],fail[N],ed[N];
    void init(){
        las=num=1;
        len[1]=-1;
        fail[0]=fail[1]=1;
    }
    int getfail(int x){
        while(s[n-len[x]-1]^s[n])x=fail[x];
        return x;
    }
    void extend(int c){
        int u=getfail(las);
        if(!son[u][c]){
            int v=++num;
            len[v]=len[u]+2;
            fail[v]=son[getfail(fail[u])][c];
            son[u][c]=num;
        }
        las=son[u][c];
        ed[n]=len[las];
    }
}pam1,pam2;
int main()
{
    scanf("%s",s+1);len=strlen(s+1);
    pam1.init();pam2.init();
    for(n=1;n<=len;++n)pam1.extend(s[n]-‘a‘);
    n=0;reverse(s+1,s+len+1);
    for(n=1;n<=len;++n)pam2.extend(s[n]-‘a‘);
    int ans=0;
    F(i,2,len)ans=max(ans,pam1.ed[i-1]+pam2.ed[len-i+1]);
    printf("%d
",ans);
    return 0;
}

A.双倍回文

维护trans指针,定义和fail不同在于有长度缩小一半的限制,求法也类似。
必须从fa的trans跳,不然复杂度可卡到(n^2) (aaaaa...) 。

B.最长双回文串

增量构造完i时,len[las]是以i结尾的最长回文串。
所以正反都建一边,枚举分界点两侧取max。

C. I Love Palindrome String

类似双倍回文,不同在于要求方案数
由fail指针定义,知某个回文串的出现次数为其在fail树上的子树siz和
拓扑统计,由于(fail[i] leq i),所以按PAM上的编号倒序扫一边即可。
特判单个字符。

D. Antisymmetry

“反对称串”是两侧对称位置的数完全不同的串
“回文串” 是两侧对称位置的数完全相同的串
考虑魔改一下PAM的判断条件。
但由于PAM的实现特别鬼畜,有很多之前精妙地避开的边界现在必须特判了。
奇数长度一定不合法,只存偶根。
偶根长度设为-1便于getfail
没有单个字符的情况,直接return

E. 对称的正方形

并不会PAM做法。
正解是二分+二维hash check
预处理三个方向的二维hash。
枚举中间点,按奇偶分类讨论。
固定中心后边长合法具有单调性,二分一半边长。
三个hash值比较check。
类似二维前缀和,预处理pow,见代码

二维hash

#include<cstdio>
#include<algorithm>
#define unll unsigned long long
#define reg register
#define F(i,a,b) for(register int (i)=(a);(i)<=(b);++(i))
using namespace std;
int read();
const int N=1005;
int n,m;
int z0[N][N],z1[N][N],z2[N][N];
unll bs1[N],bs2[N],h0[N][N],h1[N][N],h2[N][N];
void Hash(int z[N][N],unll h[N][N]){
    F(i,1,n)F(j,1,m)h[i][j]=h[i][j-1]*bs1[1]+z[i][j];
    F(i,1,n)F(j,1,m)h[i][j]+=h[i-1][j]*bs2[1];
}
unll ask(unll h[N][N],int a,int b,int x,int y){
    return h[x][y]-h[x][b-1]*bs1[y-b+1]-h[a-1][y]*bs2[x-a+1]+h[a-1][b-1]*bs1[y-b+1]*bs2[x-a+1];
}
bool check1(int x,int y,int t){
    unll w0,w1,w2;
    w0=ask(h0,x-t+1,y-t+1,x+t-1,y+t-1);
    y=m-y+1;
    w1=ask(h1,x-t+1,y-t+1,x+t-1,y+t-1);
    y=m-y+1;x=n-x+1;
    w2=ask(h2,x-t+1,y-t+1,x+t-1,y+t-1);
    return w0==w1&&w1==w2;
}
bool check2(int x,int y,int t){
    unll w0,w1,w2;
    w0=ask(h0,x-t+1,y-t+1,x+t,y+t);
    y=m-y;
    w1=ask(h1,x-t+1,y-t+1,x+t,y+t);
    y=m-y;x=n-x;
    w2=ask(h2,x-t+1,y-t+1,x+t,y+t);
    return w0==w1&&w1==w2;
}
int main(){
    n=read(),m=read();
    F(i,1,n)F(j,1,m)z0[i][j]=z1[i][m-j+1]=z2[n-i+1][j]=read()+1;
    bs1[0]=bs2[0]=1ull;
    F(i,1,m)bs1[i]=bs1[i-1]*2333ull;
    F(i,1,n)bs2[i]=bs2[i-1]*131ull;
    Hash(z0,h0);
    Hash(z1,h1);
    Hash(z2,h2);
    int ans=0;
    F(i,1,n)F(j,1,m){
        int l,r,mid;
        l=1,r=min(min(i,n-i+1),min(j,m-j+1));
        while(l>1;
            if(check1(i,j,mid))l=mid;
            else r=mid-1;
        }
        ans+=l;
        l=0,r=min(min(i,n-i),min(j,m-j));
        while(l>1;
            if(check2(i,j,mid))l=mid;
            else r=mid-1;
        }
        ans+=l;
    }
    printf("%d
",ans);
    return 0;
}
int read()
{
    int x=0,fh=1;char tc=getchar();
    while(tc<‘0‘||tc>‘9‘){if(tc==‘-‘)fh=-1;tc=getchar();}
    while(tc>=‘0‘&&tc<=‘9‘)x=(x<<3)+(x<<1)+(tc^48),tc=getchar();
    return fh*x;
}

以上是关于[专题总结]回文自动机的主要内容,如果未能解决你的问题,请参考以下文章

ZZNUOJ_C语言1107:回文数猜想(函数专题)(完整代码)

ZZNUOJ_C语言1106:回文数(函数专题)(完整代码)

回文树总结

#yyds干货盘点# 名企真题专题: 回文串

回文自动机刷题总结

PAM(回文自动机)总结