bzoj2780: [Spoj]8093 Sevenk Love Oimaster
建完sam以后自然而然的就是建后缀树了。
问题转化成多次询问一棵树(fail树)的子树中有多少不同颜色的节点。
等于一个序列的一段有多少种不同的颜色
这是个套路题,离线dfs序+树状数组可解(为什么我看了这么久愣是没看出来呢....)
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; int aa[110000]; struct SAM { int w[30],dep,fail,col; }ch[210000];int cnt,las; void insert(int dep,int col,int x) { int pre=las,now=++cnt; ch[now].dep=dep,ch[now].col=col; las=now; while(pre!=0&&ch[pre].w[x]==0) ch[pre].w[x]=now, pre=ch[pre].fail; if(pre==0)ch[now].fail=1; else { int nxt=ch[pre].w[x]; if(ch[nxt].dep==ch[pre].dep+1)ch[now].fail=nxt; else { int nnxt=++cnt; ch[nnxt]=ch[nxt]; ch[nnxt].dep=ch[pre].dep+1; ch[nxt].fail=ch[now].fail=nnxt; while(pre!=0&&ch[pre].w[x]==nxt) ch[pre].w[x]=nnxt, pre=ch[pre].fail; } } } //--------------------------------------SAM--------------------------------------------------- struct node { int x,y,next; }a[210000];int len,last[210000]; void ins(int x,int y) { len++; a[len].x=x;a[len].y=y; a[len].next=last[x];last[x]=len; } int z,L[210000],R[210000],co[210000]; void dfs(int x) { L[x]=++z;co[z]=ch[x].col; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; dfs(y); } R[x]=z; } void MakeFailTree() { len=0;memset(last,0,sizeof(last)); for(int i=2;i<=cnt;i++)ins(ch[i].fail,i); z=0,dfs(1); } //-----------------------------------------init---------------------------------------------------- /* 多次询问一棵树(fail树)的子树中有多少不同颜色的节点。 == 一个序列的一段有多少种不同的颜色 */ int s[210000]; int lowbit(int x){return x&-x;} void change(int x,int k) { while(x<=cnt+10) { s[x]+=k; x+=lowbit(x); } } int getsum(int x) { int ret=0; while(x>0) { ret+=s[x]; x-=lowbit(x); } return ret; } //-----------------------------------------calc---------------------------------------------------- char ss[110000]; struct query{int l,r,id;}q[61000];int qlen; bool cmp(query q1,query q2){return q1.r<q2.r;} int ec[11000],as[61000]; int main() { int n,Q,len; scanf("%d%d",&n,&Q); cnt=1; for(int t=1;t<=n;t++) { las=1; scanf("%s",ss+1);len=strlen(ss+1); for(int i=1;i<=len;i++) aa[i]=ss[i]-'a'+1, insert(i,t,aa[i]); } MakeFailTree(); for(int t=1;t<=Q;t++) { scanf("%s",ss+1);len=strlen(ss+1); for(int i=1;i<=len;i++)aa[i]=ss[i]-'a'+1; int now=1; bool bk=true; for(int i=1;i<=len;i++) { int x=aa[i]; if(ch[now].w[x]==0){bk=false;break;} else now=ch[now].w[x]; } if(bk==false)as[t]=0; else q[++qlen].l=L[now],q[qlen].r=R[now],q[qlen].id=t; } sort(q+1,q+qlen+1,cmp); int tp=1; memset(ec,0,sizeof(ec)); for(int i=1;i<=cnt;i++) { if(ec[co[i]]!=0)change(ec[co[i]],-1); ec[co[i]]=i,change(i,1); while(tp<=qlen&&q[tp].r==i) { as[q[tp].id]=getsum(q[tp].r)-getsum(q[tp].l-1); tp++; } } for(int i=1;i<=Q;i++)printf("%d\n",as[i]); return 0; }
多次询问一棵树(fail树)的子树中有多少不同颜色的节点。 == 一个序列的一段有多少种不同的颜色
pain and happy in the cruel world.