都快忘了在这类题的经典做法了……
将字符串一个个的插入字典树,在字典树维护好有该前缀串s的最大编号字符串j,我们记作j控制了前缀串s
对于当前的第i个字符串,维护此时有当前每个字符串控制了多少个前缀串,用一个线段树维护。
由于询问是在线的,所以用主席树来维护。于是对一个询问(l,r),在第r个线段树求一下区间(l,r)的和即为答案。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 6 using namespace std; 7 struct node{int l,r,s;} tree[200010*20]; 8 int go[100010][26],h[100010],be[100010]; 9 char s[100010]; 10 int m,t,n,q; 11 12 int build(int l,int r) 13 { 14 ++t; tree[t].l=tree[t].r=tree[t].s=0; 15 if (l==r) return t; 16 else { 17 int cur=t,m=(l+r)>>1; 18 tree[cur].l=build(l,m); 19 tree[cur].r=build(m+1,r); 20 return cur; 21 } 22 } 23 24 int work(int l,int r,int last,int x,int w) 25 { 26 ++t; tree[t]=tree[last]; 27 if (l==r) {tree[t].s+=w; return t;} 28 else { 29 int cur=t,m=(l+r)>>1; 30 if (x<=m) tree[cur].l=work(l,m,tree[last].l,x,w); 31 else tree[cur].r=work(m+1,r,tree[last].r,x,w); 32 tree[cur].s=tree[tree[cur].l].s+tree[tree[cur].r].s; 33 return cur; 34 } 35 } 36 37 int ask(int l,int r,int p,int x,int y) 38 { 39 if (x<=l&&y>=r) return tree[p].s; 40 else { 41 int m=(l+r)>>1,s=0; 42 if (x<=m) s+=ask(l,m,tree[p].l,x,y); 43 if (y>m) s+=ask(m+1,r,tree[p].r,x,y); 44 return s; 45 } 46 } 47 48 int main() 49 { 50 while (scanf("%d",&n)!=EOF) 51 { 52 t=m=0; 53 h[0]=build(1,n); 54 memset(go[0],0,sizeof(go[0])); 55 for (int i=1; i<=n; i++) 56 { 57 scanf("%s",s+1); 58 int k=0,l=strlen(s+1); 59 h[i]=h[i-1]; 60 for (int j=1; j<=l; j++) 61 { 62 int c=s[j]-'a'; 63 if (!go[k][c]) {go[k][c]=++m; memset(go[m],0,sizeof(go[m])); be[m]=0;} 64 k=go[k][c]; 65 if (be[k]) h[i]=work(1,n,h[i],be[k],-1); 66 be[k]=i; 67 } 68 h[i]=work(1,n,h[i],i,l); 69 } 70 scanf("%d",&q); 71 int z=0; 72 for (int i=1; i<=q; i++) 73 { 74 int l,r; 75 scanf("%d%d",&l,&r); 76 l=(z+l)%n+1; r=(z+r)%n+1; 77 if (l>r) swap(l,r); 78 z=ask(1,n,h[r],l,r); 79 printf("%d\n",z); 80 } 81 } 82 return 0; 83 }