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 }
View Code

题目链接: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 。
做法:广义后缀自动机。
posted @ 2016-06-03 19:28  oyzx~  阅读(660)  评论(0编辑  收藏  举报