无病呻吟:字符串算法好像还剩下后缀树。。但是他好像和后缀自动机差不多的样子(这就是你划水的原因吗?)?以及还剩下一个KMP自动机。。。但是他好像和KMP也没什么区别的样子(同上?)?以及好不容易补上了PAM这个坑。。。15年西安出了一个PAM的题,成了难题,17年哈尔滨又来了一次,这次就是中等题了。吓得我赶紧学了一波。。慌得不行
PAM:至少先学一个ACAM或者SAM,然后PAM基本上稍微看几眼就懂了了。
题意:每次给出两个串,求公共回文串个数。具体的说就是求四元组的个数,合法的四元组(s1,t1,s2,t2)定义为:串1的[ s1, t1 ]子串是一个回文串,且串2的[ s2 , t2 ]子串是一个回文串,且两段子串相同。
题解:对串1和串2构造两个PAM,然后要记住PAM有两个起点0和1,分别对应于长度为奇数和偶数的起点。然后同时往下dfs,统计答案是两个PAM的cnt乘积。串1的cnt对应于s1,t1的选择数,串2 的cnt对应于s2,t2的选择数。
Code:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+100;
struct PAM{
int nxt[maxn][26],len[maxn],cnt[maxn],fail[maxn];
int S[maxn];
int last,p,now;
int newnode(int l){
memset(nxt[p],0,sizeof nxt[p]);
cnt[p]=0;
len[p]=l;
return p++;
}
void init(){
p =0;
newnode(0);
newnode(-1);
last =0;
now =0;
S[now++] =-1;
fail[0]=1;
}
inline int get_fail(int x){
int tx =x;
while (S[now-len[tx]-2]!=S[now-1]) tx = fail[tx];
return tx;
}
void add(int c){
S[now++] =c;
int cur = get_fail(last);
if (!nxt[cur][c]){
int tt = newnode(len[cur]+2);
fail[tt] = nxt[get_fail(fail[cur])][c];
nxt[cur][c] =tt;
}
last = nxt[cur][c];
cnt[last]++;
}
void count(){
for (int i=p-1;i>=0;i--){
cnt[fail[i]]+=cnt[i];
}
cnt[0]=cnt[1]=0;
}
}pam1,pam2;
long long dfs(int u,int v){
long long res =0;
for (int i=0;i<26;i++){
int uu = pam1.nxt[u][i];
int vv = pam2.nxt[v][i];
if (uu&&vv){
res +=1LL*pam1.cnt[uu]*pam2.cnt[vv];
res+=dfs(uu,vv);
}
}
return res;
}
int T;
int Cas=1;
int len1,len2;
char s1[maxn],s2[maxn];
int main(){
scanf("%d",&T);
while (T--){
pam1.init();
pam2.init();
scanf("%s%s",s1,s2);
len1 = strlen(s1);
len2 = strlen(s2);
for (int i=0;i<len1;i++){
pam1.add(s1[i]-'a');
}
for (int i=0;i<len2;i++){
pam2.add(s2[i]-'a');
}
pam1.count();
pam2.count();
printf("Case #%d: %I64d\n",Cas++,dfs(0,0)+dfs(1,1));
}
return 0;
}