拖了很久才补的回文树,感觉网上的博客都是一个做法。。回文树统计不同种类的回文串出现次数,然后用字符串hash来判每个回文子串是否符合要求
#include<bits/stdc++.h> using namespace std; #define maxn 300005 #define mod 19260817 #define ll long long #define P 131 char s[maxn]; ll len,F[maxn],h[maxn]; ll has(int l,int r){ return (h[r]-h[l-1]*F[r-l+1]%mod+mod)%mod; } struct PAM{ int nxt[maxn][26],fail[maxn]; int len[maxn],S[maxn]; int cnt[maxn],num[maxn]; int n,p,last,id[maxn];//记录第i个结点的后缀下标 int newnode(int l){ memset(nxt[p],0,sizeof nxt[p]); len[p]=l; cnt[p]=num[p]=0; return p++; } void init(){ memset(cnt,0,sizeof cnt); memset(num,0,sizeof num); p=0; newnode(0); newnode(-1); last=n=0; S[0]=-1; fail[0]=1; } int get_fail(int x){ while(S[n-len[x]-1]!=S[n])x=fail[x]; return x; } void add(int c){ c-='a'; S[++n]=c; int cur=get_fail(last); if(!nxt[cur][c]){ int now=newnode(len[cur]+2); fail[now]=nxt[get_fail(fail[cur])][c]; nxt[cur][c]=now; num[now]=num[fail[now]]+1; } last=nxt[cur][c]; cnt[last]++; id[last]=n; } ll ans[maxn]; ll count(){ memset(ans,0,sizeof ans); for(int i=p-1;i>=0;i--)cnt[fail[i]]+=cnt[i]; for(int i=2;i<p;i++){ int L=id[i]-len[i],R=id[i]-1; int mid=(L+R)/2; ll tmp1=has(L,mid)%mod; ll tmp2; if(len[i]%2==0) tmp2=has(mid+1,R)%mod; else tmp2=has(mid,R)%mod; if(tmp1==tmp2) ans[len[i]]+=cnt[i]; } } }tr; int main(){ F[0]=1; for(int i=1;i<=300000;i++)F[i]=F[i-1]*P%mod; while(scanf("%s",&s)!=EOF){ tr.init(); len=strlen(s); for(int i=0;i<len;i++) tr.add(s[i]); h[0]=s[0]; for(int i=1;i<len;i++) h[i]=(h[i-1]*P%mod+s[i])%mod; tr.count(); for(int i=1;i<len;i++) cout<<tr.ans[i]<<" "; cout<<tr.ans[len]<<' '; } }