HDU6599 I Love Palindrome String(回文树)
/* * hdu6599 * 题意: * 判断长度从1到len的子串中有多少回文串,且前一半也为回文 * 题解; * 用回文树求出本质不同的回文串,对每个回文串的前一半再判断是否为回文 */ #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef unsigned long long ll; const int maxn=3e5+100; int a[maxn]; ll f[maxn]; ll p[maxn]; ll cal (int l,int r) { if (l==0) return f[r]; return f[r]-f[l-1]*p[r-l+1]; } bool check (int l,int r) { int mid=(l+r)>>1; if ((r-l+1)&1) return cal(l,mid)==cal(mid,r); else return cal(l,mid)==cal(mid+1,r); //分别判断 } struct palin_Tree { //回文树 int nxt[maxn][26]; int fail[maxn]; int len[maxn]; int cnt[maxn]; int S[maxn]; int ind[maxn]; int last,id,n; /* * 一个节点一个本质不同的回文串 * len[i]表示回文串i的长度 * nxt[i][c]表示编号为i的节点表示的回文串在两边添加字符c以后变成的回文串的编号 * cnt[i]表示节点i表示的本质不同的串的个数 * fail[i]表示节点i失配以后跳转不等于自身的节点i表示的回文串的最长后缀回文串 * last表示新添加一个字母后形成的最长回文串表示的节点 * S[i]表示第i次添加的字符 * ind[i]表示第i号节点是插入到第ind[i]号字符串产生的节点 */ int newNode (int x) { for (int i=0;i<26;i++) nxt[id][i]=0; cnt[id]=0; len[id]=x; return id++; } void init () { id=0; newNode(0); newNode(-1); fail[0]=1; S[0]=-1; last=n=0; } int getfail (int x) { while (S[n-len[x]-1]!=S[n]) x=fail[x]; return x; } void insert (int c) { c-='a'; S[++n]=c; int cur=getfail(last); if (!nxt[cur][c]) { int u=newNode(len[cur]+2); fail[u]=nxt[getfail(fail[cur])][c]; nxt[cur][c]=u; } last=nxt[cur][c]; cnt[last]++; ind[last]=n; } void getsum () { for (int i=id-1;i>=0;i--) cnt[fail[i]]+=cnt[i]; for (int i=2;i<id;i++) if (check(ind[i]-len[i],ind[i]-1)) a[len[i]]+=cnt[i]; } }Palin_Tree; char s[maxn]; int main () { p[0]=1; for (int i=1;i<maxn;i++) p[i]=p[i-1]*13331; while (~scanf("%s",s)) { int len=strlen(s); for (int i=0;i<=len;i++) a[i]=0; Palin_Tree.init(); for (int i=0;i<len;i++) Palin_Tree.insert(s[i]); f[0]=s[0]; for (int i=1;i<len;i++) f[i]=f[i-1]*13331+s[i]; Palin_Tree.getsum(); for (int i=1;i<=len;i++) printf("%d%c",a[i]," \n"[i==len]); } }