HDU 4641 K-string 后缀自动机 并查集

Posted 鲸头鹳

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 4641 K-string 后缀自动机 并查集相关的知识,希望对你有一定的参考价值。

http://acm.hdu.edu.cn/showproblem.php?pid=4641

https://blog.csdn.net/asdfgh0308/article/details/40969047

给一个小写字母字符串,1 a表示在字符串尾部添加一个小写字母a,2 表示当前有多少种子串出现次数大于等于K。

求出现次数桶排序(说是拓扑排序也可以?)就阔以了,种类就是t[i].len-t[t[i].f].len。

在线处理是直接扫描,时间复杂度是O(树高*m)。

离线做法是先把所有添加操作都做完,然后去掉字母反向求子串种数,可以使用并查集降低时间复杂度(每个连通块中只有总父亲表示的子串对当前答案有贡献,保证了每个点的平均扫描次数)。

技术分享图片
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 #include<queue>
 7 using namespace std;
 8 #define LL long long
 9 const int maxn=60100;
10 int n,m,K;
11 char ch[maxn*10],ch1[3];
12 int e[maxn*4]={},b[maxn*5]={},siz;
13 struct nod{
14     int sig[26],f,len;
15 }t[maxn*10]; int tot,la;
16 
17 int cnt[maxn*10]={},rk[maxn*10]={};
18 int fa[maxn*10]={},sub[maxn*10]={},g[maxn*10]={};
19 LL ans[maxn*4]={};
20 
21 void addit(int z){
22     int x=++tot,i=la; t[x].len=t[i].len+1;
23     b[siz]=x;
24     for(;i&&!t[i].sig[z];i=t[i].f)t[i].sig[z]=x;
25     if(!i)t[x].f=1;
26     else{
27         int y=t[i].sig[z];
28         if(t[y].len==t[i].len+1)t[x].f=y;
29         else{
30             int p=++tot;
31             t[p]=t[y];t[p].len=t[i].len+1;
32             t[y].f=t[x].f=p;
33             for(;i&&t[i].sig[z]==y;i=t[i].f)t[i].sig[z]=p;
34         }
35     }
36     la=x;
37 }
38 void msort(){
39     int i;
40     for(i=0;i<=siz;i++)cnt[i]=0;
41     for(i=2;i<=tot;i++)cnt[t[i].len]++;
42     for(i=1;i<=siz;i++)cnt[i]+=cnt[i-1];
43     for(i=tot;i>1;i--)rk[cnt[t[i].len]--]=i;
44 }
45 int getfa(int x){
46     int y=x,z;
47     while(y!=fa[y])y=fa[y];
48     while(x!=fa[x]){ z=x; x=fa[x];fa[z]=y;}
49     return fa[x];
50 }
51 int main(){
52     int i,p,y; LL num;
53     while(~scanf("%d%d%d",&n,&m,&K)){
54         memset(t,0,sizeof(t)); tot=1; la=1; siz=0;
55         scanf("%s",ch);
56         for(i=0;i<n;++i){ addit(ch[i]-a); ++siz; }
57         for(i=1;i<=m;i++){
58             scanf("%d",&e[i]); 
59             if(e[i]==1){ scanf("%s",ch1); addit(ch1[0]-a); ch[siz++]=ch1[0]; }
60         }
61         msort(); p=1,num=0;
62         for(i=1;i<=tot;i++){ g[i]=0; fa[i]=i; sub[i]=0;}
63         for(i=0;i<siz;++i){ p=t[p].sig[ch[i]-a]; ++g[p]; }
64         for(i=tot-1;i>0;i--){ p=rk[i]; if(t[p].f!=1)g[t[p].f]+=g[p]; }
65         for(i=2;i<=tot;++i){ if(g[i]>=K) num+=t[i].len-t[t[i].f].len; }
66         for(i=m;i>0;i--){
67             if(e[i]==2)ans[i]=num;
68             else{
69                 p=b[--siz];
70                 y=getfa(p);
71                 while(y!=1&&g[y]<K){ fa[y]=getfa(t[y].f); y=fa[y];}
72                 y=getfa(p);
73                 if(y==1)continue;
74                 sub[y]++;
75                 while(y!=1&&(g[y]-sub[y]<K)){
76                     num-=t[y].len-t[t[y].f].len;
77                     p=getfa(t[y].f);
78                     sub[p]+=sub[y]; fa[y]=p;
79                     y=fa[y];
80                 }
81             }
82         }
83         for(i=1;i<=m;++i)if(e[i]==2)printf("%lld\n",ans[i]);
84     }
85     return 0;
86 }
View Code

 

以上是关于HDU 4641 K-string 后缀自动机 并查集的主要内容,如果未能解决你的问题,请参考以下文章

HDU 4641 K-string 后缀自动机 并查集

HDU 6096 AC自动机

hdu4622(后缀自动机模板)

HDU 4622 Reincarnation(后缀自动机)

HDU4622Reincarnation(后缀自动机)

hdu4622-Reincarnation(后缀自动机)