题目大意
给两个字符串。求满足要求的四元组(i,j,x,y)个数。
要求①,A[i..j]=B[x..y]。要求②,A[i..j]是回文串。
其中
|A|,|B|≤50000
|
A
|
,
|
B
|
≤
50000
。
解题思路
一开始想的部分分用SAM处理B。然后
O(|A|2)
O
(
|
A
|
2
)
暴力得出结果。不知道哪里打挂了。
对于A=B的部分分,我想都没想直接上马拉车。后来才发现答案也和SAM的点的right集有关。
结果可想而知。
正解?
回文自动机。
计算一下每个回文串在A,B里分别出现了几次。然后乘起来就好。
在这里口胡一下回文自动机咋搞。
首先,对于字符串S的回文自动机中的每个点存的是本质不同的回文串。
注:对于串ABABA,其中ABA(1..3)和ABA(3..5)是本质相同的回文串。
回文自动机里有2种边:
①trans(x,c)。对于一个回文串x,通过trans(x,c)转移到cxc。即x的左右各添个字符c。
②fail[x]。fail[x]连向x的最长回文后缀。
考虑增量法构建回文自动机。
对于一个字符串S,要增加一个字符串c,如果直接加不能够使得S’=(S+c)成为回文串,那么跳fail边,直到跳到某个点对应的串A,这个A是目前S的一个后缀(这很显然),加上字符c之后又能够产生一个新的回文串B。
则len[B]=len[A]+2.
//跳的时候要从F(fail[np])开始跳……
其实原理和AC自动机差不多。
时间复杂度O(n).
原因:每增加一个字符,最多只会多出1个本质不同的回文子串
代码(裸的回文串)
struct PAM{
int pam[N][26],len[N],st[N],fail[N],cnt[N],gs,L,wz;
PAM(){len[1]=-1;fail[0]=1;st[0]=-1;gs=1;}
int F(int x){for(;st[L-len[x]-1]!=st[L];x=fail[x]);return x;}
void Extend(int c){
st[++L]=c;
int np=F(wz),nq;
if(!pam[np][c]){
len[nq=++gs]=len[np]+2;
fail[nq]=pam[F(fail[np])][c];
pam[np][c]=nq;
}
}
void Work(){
char ch=getchar();
while(ch<'A'||ch>'Z')ch=getchar();
while(ch>='A'&&ch<='Z')Extend(ch-'A'),ch=getchar();
}
}