n<=100000个字符串,总长度<=100000,问每个字符串有多少子串至少出现在n个串中的m个。
方法一:(未写)串在一起,后缀数组搞出来,然后height数组--排名相邻两个后缀的lcp搞出来,然后可能产生贡献的就是一段连续的height。对这段连续的height,如果有区间[L,R],满足R-L+1>=m-1,那么可以把[L,R]的后缀加上数字min(height_i),L<=i<=R,最后对每个字符串查询其所有后缀被+的次数。那优雅的做法就是看每个数的贡献,Li,Ri表示最大的区间满足height_i<=height_j,Li<=j<=Ri,然后记min(height_((Li)-1),height_((Ri)+1))=Gi,这个数的贡献就是:使得[Li,Ri]中的每个数加上了height_i-Gi。Li,Ri可用单调栈求出,区间加可用树状数组。
不过有个小问题,如果一个后缀“越位”,即跨越到下一个字符串了,怎么办?那么在求height的时候留个心眼,令Hi表示第i个后缀到达其对应字符串末尾的长度,那么height(i+1,i)算出来后再min一下Hi和Hi+1即可。
方法二:后缀自动机搞出来,看每个状态的贡献。先算出现次数。
(1)沿着parent树做set的启发式合并。
(2)每个字符串在自动机里走一发,每走一步沿parent更新一次。这样好像最坏L^(3/2)?
接下来按pos从小到大算一次:每个状态沿着parent链走能得到多少出现次数>=m的字符串,这其实就是一个状态的贡献,因为每个状态代表的字符串包含了他的parent们。其实直接dfs一次就行,我写的太矫情还排了个序。
1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 #include<stdlib.h> 5 //#include<iostream> 6 //#include<assert.h> 7 //#include<time.h> 8 using namespace std; 9 10 int n,m; 11 #define maxn 200011 12 struct samnode 13 { 14 samnode *ch[26],*pre; 15 int pos,vis,tot,f; 16 samnode() {memset(ch,0,sizeof(ch)); pre=NULL; vis=tot=f=0;} 17 }; 18 struct SAM 19 { 20 samnode *root,*last; 21 SAM() {root=new samnode; root->pos=0; last=root;} 22 int idx(char c) {return c-'a';} 23 void insert(char c,int p) 24 { 25 int id=idx(c);samnode *x=new samnode; 26 x->pos=p; 27 samnode *y=last; 28 for (;y!=NULL && y->ch[id]==NULL;y=y->pre) y->ch[id]=x; 29 last=x; 30 if (y==NULL) x->pre=root; 31 else 32 { 33 if (y->ch[id]->pos==y->pos+1) x->pre=y->ch[id]; 34 else 35 { 36 samnode *z=y->ch[id],*w=new samnode; 37 memcpy(w->ch,z->ch,sizeof(z->ch)); 38 w->pos=y->pos+1; w->pre=z->pre; 39 z->pre=x->pre=w; 40 for (;y!=NULL && y->ch[id]==z;y=y->pre) y->ch[id]=w; 41 } 42 } 43 } 44 }sam; 45 46 char s[maxn];int end[maxn]; 47 samnode *tmp[maxn];int lt=0; 48 bool cmp(const samnode *a,const samnode *b) {return a->pos<b->pos;} 49 50 void dfs(samnode *x) 51 { 52 tmp[++lt]=x;x->vis=n+1; 53 for (int i=0;i<26;i++) if (x->ch[i]!=NULL && x->ch[i]->vis!=n+1) dfs(x->ch[i]); 54 } 55 void dfs() {dfs(sam.root);} 56 57 int main() 58 { 59 scanf("%d%d",&n,&m); 60 for (int i=1;i<=n;i++) 61 { 62 scanf("%s",s+end[i-1]+1); 63 end[i]=end[i-1]+strlen(s+end[i-1]+1); 64 sam.last=sam.root; 65 for (int j=end[i-1]+1;j<=end[i];j++) sam.insert(s[j],j-end[i-1]); 66 } 67 for (int i=1;i<=n;i++) 68 { 69 samnode *p=sam.root; 70 for (int j=end[i-1]+1;j<=end[i];j++) 71 { 72 p=p->ch[s[j]-'a']; 73 for (samnode *q=p;q!=NULL && q->vis!=i;q=q->pre) q->vis=i,q->tot++; 74 } 75 } 76 dfs(); 77 sort(tmp+1,tmp+1+lt,cmp); 78 tmp[1]->f=0; 79 for (int i=2;i<=lt;i++) tmp[i]->f=tmp[i]->pre->f+(tmp[i]->tot>=m?tmp[i]->pos-tmp[i]->pre->pos:0); 80 #define LL long long 81 for (int i=1;i<=n;i++) 82 { 83 LL ans=0; 84 samnode *p=sam.root; 85 for (int j=end[i-1]+1;j<=end[i];j++) 86 { 87 p=p->ch[s[j]-'a']; 88 ans+=p->f; 89 } 90 printf("%lld ",ans); 91 } 92 return 0; 93 }