「专题总结」回文自动机

模板

#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;
}
原文地址:https://www.cnblogs.com/hzoi-yzh/p/12101095.html