BZOJ3473: 字符串

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

 

转载于:https://www.cnblogs.com/Blue233333/p/7994494.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值