zoukankan      html  css  js  c++  java
  • 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
    ",ans[i]);
    84     }
    85     return 0;
    86 }
    View Code

  • 相关阅读:
    redhat,centos Linux常用命令LS之常用功能
    人生信用卡
    如何让Redhat Linux启动时进入字符终端模式(不进入XWindow)
    OpenJDK和JDK区别
    Linux rpm 命令参数使用详解[介绍和应用]
    linux 的vim命令详解
    centos6.4安装javajdk1.8
    samba服务器 实现Linux与windows 文件共享
    SELinux 宽容模式(permissive) 强制模式(enforcing) 关闭(disabled) 几种模式之间的转换
    linux学习之 Linux下的Eclipse安装
  • 原文地址:https://www.cnblogs.com/137shoebills/p/8968931.html
Copyright © 2011-2022 走看看