2018ICPC南京/gym101981 M-Mediocre String Problem 回文自动机+扩展kmp
2018ICPC南京/gym101981 M-Mediocre String Problem
题意
给定两个字符串\(s\)和\(t\),计算有多少个三元组\((i,j,k)\)满足如下条件:
-
\(1\le i \le j \le |s|\)
-
\(1\le k \le |t|\)
-
\(j-i+1>k\)
-
\(s[i;j]+t[1;k]\)是一个回文串
分析
因为\(j-i+1>k\),所以第四个条件可以看作满足\(s[i;i+k-1]+t[1,k]\)是一个回文串且\(s[i+k;j]\)是一个回文串,将\(s\)反转一下就是\(s[i;j-k]\)是一个回文串且\(s[j-k+1;j]=t[1,k]\),那么思路就很显然了,令\(z[i]\)为后缀\(s[i;n]\)和\(t\)的最长公共前缀长度,利用扩展\(kmp\)可以在\(O(n)\)时间复杂度求出\(z\),对\(s\)建回文自动机,可以求出数组\(cnt\),\(cnt[i]\)表示\(s\)中以\(i\)结尾的回文子串个数,答案即为\(\sum_{i=1}^{n-1}cnt[i]\cdot z[i+1]\)。
Code
#include<bits/stdc++.h>
#define rep(i,x,n) for(int i=x;i<=n;i++)
#define per(i,n,x) for(int i=n;i>=x;i--)
#define sz(a) int(a.size())
#define rson mid+1,r,p<<1|1
#define pii pair<int,int>
#define lson l,mid,p<<1
#define ll long long
#define pb push_back
#define mp make_pair
#define se second
#define fi first
using namespace std;
const double eps=1e-8;
const int mod=1e9+7;
const int N=2e6+10;
const int inf=1e9;
int z[N],n,m,len;
char s[N],t[N],ts[N];
int now,dp[N],g[N];
struct PAM{
int ch[N][26],fail[N],len[N],cnt[N],tot,last;
int newnode(int x){
++tot;
memset(ch[tot],0,sizeof ch[tot]);
fail[tot]=0;len[tot]=x;
return tot;
}
void init(){
tot=-1;newnode(0);newnode(-1);
fail[0]=1;
last=0;
}
int gao(int x){
while(s[now-len[x]-1]!=s[now]) x=fail[x];
return x;
}
void insert(){
int p=gao(last);
int c=s[now]-'a';
if(!ch[p][c]){
int tmp=ch[gao(fail[p])][c];
ch[p][c]=newnode(len[p]+2);
fail[tot]=tmp;
cnt[tot]=cnt[tmp]+1;
}
last=ch[p][c];
}
ll qy(){
ll ans=0;
for(now=1;now<=n;now++){
insert();
ans+=1ll*cnt[last]*z[m+2+now];
}
return ans;
}
}P;
void Z(){
for(int i=2,l=1,r=1;i<=n+m+1;i++){
if(i<=r) z[i]=min(r-i+1,z[i-l+1]);
while(i+z[i]<=n+m+1&&ts[z[i]+1]==ts[i+z[i]]) ++z[i];
if(i+z[i]-1>r) l=i,r=i+z[i]-1;
}
}
int main(){
cin>>s+1>>t+1;
n=strlen(s+1);
m=strlen(t+1);
rep(i,1,m) ts[++len]=t[i];
ts[++len]='#';
reverse(s+1,s+n+1);
rep(i,1,n) ts[++len]=s[i];
Z();
P.init();
printf("%lld\n",P.qy());
return 0;
}