[专题总结]回文自动机
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:回文数猜想(函数专题)(完整代码)