[JSOI2013]快乐的 JYY
题意:
题面很啰嗦,大致意思就是:给你两个字符串 \(S,T\),求有多少对 \(S\) 的子串和 \(T\) 的子串是相同的字符串且是回文串。
输入:PUPPY PUPPUP
输出:17
额,manacher傻乎乎的,没法解决回文串出现次数这类的问题,所以我们用 \(PAM\)。
其实很简单,不要想太多。
\(PAM\) 的每一个点表示一个本质不同的回文串,那么我们如果知道这个串在 \(S\) 中的出现次数和在 \(T\) 中的出现次数乘起来就是这个串的答案。
那么我们将两个字符串分别建一个 \(PAM\) ,统计答案时将两个 \(PAM\) 同时遍历,保证遍历的两个位置表示的是同一个串,如果有任何一个没有往下走的路:\(ch[x][i]==0 || ch[y][i]==0\),那么就找别的路,因为有一个字符串中没有相应的回文串那么以后更长的回文串也不可能产生贡献了。
由于奇数长度的回文树和偶数长度的回文树独立且互不交叉,所以分别遍历一遍,将所有乘积加起来即为答案。
怎样统计出现次数,只需要沿着 \(fail\) 树从深到浅拓扑就行了,因为 \(fail\) 树上的点是按相同的右端点连接的,将同一个右端点最长的回文串开始将所有回文串次数加一,可以保证不重不漏。
遇到要建两个或者多个 \(PAM\) 的题,用结构体真香。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#define QWQ cout<<"QwQ"<<endl;
#define ll long long
#include <vector>
#include <queue>
#include <stack>
#include <map>
using namespace std;
const int N=101010;
const int qwq=303030;
const int inf=0x3f3f3f3f;
ll ans;
struct PAM{
int n;
char s[N];
int now,cnt=1;
int len[N],ch[N][26],fail[N];
ll siz[N];
int getfail(int i,int x) {
while(s[i-len[x]-1] != s[i]) x = fail[x];
return x;
}
void built() {
n = strlen(s+1);
len[1] = -1; fail[0] = 1;
s[0] = '#';
for(int i=1;i<=n;i++) {
int x = getfail(i,now), c = s[i]-'A';
if(!ch[x][c]) {
len[++cnt] = len[x] + 2;
fail[cnt] = ch[ getfail(i,fail[x]) ][c];
ch[x][c] = cnt;
}
now = ch[x][c];
siz[now]++;
}
for(int i=cnt;i>=2;i--)
siz[ fail[i] ] += siz[i];
}
} P,Q;
void DFS(int p,int q) {
if(p>1) ans += P.siz[p] * Q.siz[q];
for(int i=0;i<26;i++)
if(P.ch[p][i] && Q.ch[q][i])
DFS(P.ch[p][i],Q.ch[q][i]);
}
int main() {
scanf("%s",P.s+1); P.built();
scanf("%s",Q.s+1); Q.built();
DFS(1,1);
DFS(0,0);
cout<<ans;
return 0;
}
首先记得 \(0\) 号点和 \(1\) 号点是不能算入贡献的。
然后要记得开\(long\ long\)。。。