题意

给n个字符串,两两拼接,问拼接后的\(n\times n\)个字符串中有多少个回文串。

分析

将所有正串插入字典树中,马拉车跑出所有串哪些前缀和后缀为回文串,记录位置,用反串去字典树中查询,两字符串拼成回文串有三种情况:

  • 未匹配完,反串后缀是回文串。

  • 匹配结束,正串后缀是回文串。

  • 匹配结束,正串等于反串。

字典树中维护当前位置有多少正串和当前位置有多少后缀为回文串的正串。

Code

#include<cstring>
#include<cstdio>
#include<vector>
#include<iostream>
#include<algorithm>
#include<map>
#define fi first
#define se second
#define pb push_back
#define lson l,mid,p<<1
#define rson mid+1,r,p<<1|1
#define ll long long
using namespace std;
const int inf=1e9;
const int mod=1e9+7;
const int maxn=2e6+10;
int p[maxn*2],f[2][maxn],len[maxn],sum[2][maxn];
char s[maxn],t[maxn*2];
int son[maxn][26],tot;
ll ans;
void ins(int dl,int dr){
    int rt=0;
    for(int i=dl;i<=dr;i++){
        if(f[1][i]) sum[1][rt]++;
        if(!son[rt][s[i]-'a']) son[rt][s[i]-'a']=++tot;
        rt=son[rt][s[i]-'a'];
        if(i==dr) sum[0][rt]++;
    }
}
void qy(int dl,int dr){
    int rt=0;
    for(int i=dr;i>=dl;i--){
        if(f[0][i]) ans+=sum[0][rt];
        if(!son[rt][s[i]-'a']) return;
        rt=son[rt][s[i]-'a'];
        if(i==dl) ans+=sum[1][rt]+sum[0][rt];
    }
}
int mlc(int dl,int dr){
    int m=0,p0=1;p[1]=1;t[++m]='#';
    for(int i=dl;i<=dr;i++){
        t[++m]=s[i];
        t[++m]='#';
    }
    for(int i=2;i<=m;i++){
        int j=min(p[p0]+p0-i,p[2*p0-i]);
        if(i+j<p[p0]+p0){
            p[i]=j;
        }else{
            while(i-j>=1&&i+j<=m&&t[i-j]==t[i+j]) ++j;
            p[i]=j;p0=i;
        }
    }
    for(int i=dl;i<dr;i++){
        if(p[i-dl+2]-1==i-dl+1) f[0][i]=1;
        if(p[dr-2*dl+i+3]-1==dr-i) f[1][i+1]=1;
    }
}
int main(){
	//ios::sync_with_stdio(false);
	//freopen("in","r",stdin);
	int n;
	scanf("%d",&n);int l=1;
	for(int i=1,m;i<=n;i++){
		scanf("%d%s",&len[i],s+l);
        mlc(l,l+len[i]-1);
        ins(l,l+len[i]-1);
        l+=len[i];
	}l=1;
	for(int i=1;i<=n;i++){
        qy(l,l+len[i]-1);
        l+=len[i];
	}
	cout<<ans<<'\n';
	return 0;
}