bzoj3756: Pty的字符串
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 #define maxn 800005 7 #define maxm 1600005 8 #define maxl 1600005 9 #define ll long long 10 using namespace std; 11 int n,cnt,m,last,root,tot,val[maxl],now[maxn],son[maxl],prep[maxl],ri[maxm],fa[maxm],dist[maxm],tmp[maxm]; 12 ll sum[maxm],ans; 13 char st[maxn*20]; 14 struct Tsegment{ 15 int son[maxm][3]; 16 void prepare(){tot=last=root=1,memset(ri,0,sizeof(ri)),memset(son,0,sizeof(son)),memset(dist,0,sizeof(dist));} 17 int newnode(int x){ 18 dist[++tot]=x; return tot; 19 } 20 int add(int x,int p){ 21 int q=son[p][x]; 22 if (q==0){ 23 int np=newnode(dist[p]+1); last=np; ri[np]=1; 24 for (;p&&!son[p][x];p=fa[p]) son[p][x]=np; 25 if (p==0) fa[np]=root; 26 else{ 27 int q=son[p][x]; 28 if (dist[p]+1==dist[q]) fa[np]=q; 29 else{ 30 int nq=newnode(dist[p]+1); 31 memcpy(son[nq],son[q],sizeof(son[q])); 32 fa[nq]=fa[q],fa[q]=fa[np]=nq; 33 for (;p&&son[p][x]==q;p=fa[p]) son[p][x]=nq; 34 } 35 } 36 }else{ 37 if (dist[p]+1==dist[q]) last=q,ri[q]++; 38 else{ 39 int nq=newnode(dist[p]+1); last=nq; ri[nq]=1; 40 memcpy(son[nq],son[q],sizeof(son[q])); 41 fa[nq]=fa[q],fa[q]=nq; 42 for (;p&&son[p][x]==q;p=fa[p]) son[p][x]=nq; 43 } 44 } 45 return last; 46 } 47 void Tsort(){ 48 memset(sum,0,sizeof(sum)); 49 for (int i=1;i<=tot;i++) sum[dist[i]]++; 50 for (int i=1;i<=n;i++) sum[i]+=sum[i-1]; 51 for (int i=1;i<=tot;i++) tmp[sum[dist[i]]--]=i; 52 memset(sum,0,sizeof(sum)); 53 for (int x,i=tot;i>=1;i--){ 54 x=tmp[i]; 55 if (fa[x]) ri[fa[x]]+=ri[x]; 56 } 57 ri[root]=0,dist[root]=dist[0]=0; 58 for (int x,i=1;i<=tot;i++){ 59 x=tmp[i]; 60 sum[x]=ri[x]*(dist[x]-dist[fa[x]]); 61 if (fa[x]) sum[x]+=sum[fa[x]]; 62 } 63 sum[root]=0; 64 } 65 void work(){ 66 int len=0; last=root; ans=0; 67 for (int x,i=1;i<=m;i++){ 68 x=st[i]-'a'; 69 if (son[last][x]) len++,last=son[last][x]; 70 else{ 71 for (;last&&!son[last][x];last=fa[last]); 72 if (last==0) last=root,len=0; 73 else{ 74 len=dist[last]+1,last=son[last][x]; 75 } 76 } 77 if (last!=root&&last){ 78 ans+=(sum[fa[last]]+(len-dist[fa[last]])*ri[last]); 79 } 80 } 81 printf("%lld\n",ans); 82 } 83 }SAM; 84 struct Graph{ 85 void add(int x,int y,int z){ 86 cnt++,prep[cnt]=now[x],now[x]=cnt,son[cnt]=y,val[cnt]=z; 87 } 88 void dfs(int x,int goal){ 89 for (int i=now[x],so=son[i];i;i=prep[i],so=son[i]){ 90 int p=SAM.add(val[i],goal); 91 dfs(so,p); 92 } 93 } 94 }G; 95 int main(){ 96 scanf("%d",&n); 97 cnt=ans=0,memset(now,0,sizeof(now)); 98 for (int i=2,u,v;i<=n;i++){ 99 scanf("%d%s",&u,st+1); 100 G.add(u,i,st[1]-'a'); 101 } 102 SAM.prepare(); 103 G.dfs(1,1); 104 SAM.Tsort(); 105 scanf("%s",st+1),m=strlen(st+1); 106 SAM.work(); 107 return 0; 108 }
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3756
题目大意:
在神秘的东方有一棵奇葩的树,它有一个固定的根节点(编号为1)。树的每条边上都是一个字符,字符为a,b,c中的一个,你可以从树上的任意一个点出发,然后沿着远离根的边往下行走,在任意一个节点停止,将你经过的边的字符依次写下来,就能得到一个字符串,例如:
在这棵树中我们能够得到的字符串是:
c, cb, ca, a, b, a
现在pty得到了一棵树和一个字符串S。如果S的一个子串[l,r]和树上某条路径所得到的字符串完全相同,则我们称这个子串和该路径匹配。现在pty想知道,S的所有子串和树上的所有路径的匹配总数是多少?
做法:看到此题,我们可以发现,先将题目中给定的trie树建立一个广义后缀自动机,预处理出每个点的right值,dist值,sum值,前两个都是后缀自动机自带的,不用说了,sum表示parent树中该点到根节点的路径上所有的字符串的数目(能使后面的复杂度降至O(n)级别的),然后用输入的字符串在SAM上匹配即可,与普通匹配相似,但是统计答案不一样,ans+=(sum【fa【x】】+right【x】*(len-dist【x】)),x表示走到的点,len表示匹配的长度,为什么呢?除了要将走到的状态带来的答案外,parent树中这个点的祖先节点都能完全匹配到,所以要加上sum【fa【x】】。记得sum和ans要开long long 。
做法:广义后缀自动机。