zoukankan      html  css  js  c++  java
  • BZOJ 3413 匹配 (后缀自动机+线段树合并)

    题目大意:

    懒得概括了

    神题,搞了2个半晚上,还认为自己的是对的...一直调不过,最后终于在jdr神犇的帮助下过了这道题

    线段树合并该是这道题最好理解且最好写的做法了,貌似主席树也行?但线段树合并这个算法实在是太优美了

    一个模式串从左到右为开头进行匹配,如果在前面已经匹配成功了,后面就算能匹配成功也没用

    因此在$parent$树里维护一个数组$mi_{x}$,表示在$parent$树中,节点$x$的子树中$len_{x}$的最小值,可以用桶+拓扑$O(n)$实现

    如果一个模式串$T$是$S$的一个子串

    首先用上面维护的$mi_{x}$数组找出这个串能被匹配上的,第一个末尾位置$pos$

    显然,以$[1,pos-len]$为开头,向后进行暴力匹配,都匹配不出$T$,每个位置为开头都失配一次,失配的总长度是$pos-len$

    接下来就是解决以$[1,pos-len]$为开头,能匹配上$T$的一小部分前缀的情况了

    直接讨论每个位置最多能往后匹配多长,会很复杂(如果大家想看这种做法可以看大师的博客)

    转化问题

    我们讨论$T$的每个前缀,在$S$一定范围内的前缀中,作为后缀出现几次不就行了

    我们把$T$串放到$trs$图里跑

    现在走到了一个节点$x$,已经走过的路径长度是$i$,它的$right$集合可以用线段树合并预处理出来,我们只需要求出$x$的$parent$子树内,$len$小于某个上限的$endpos$节点数量就行了

    推导可得,这个上限是$pos-len+i$,因为再往后就会超出第一次匹配的位置,不可行

    如果$T$不是$S$的一个子串,失配长度就是$n$,上限也全都改成$n$就行了

    而且$endpos$节点的$len$互不相同,恰好契合了线段树合并的性质,预处理的时候从叶节点一直往上合并即可

      1 #include <cmath>
      2 #include <vector>
      3 #include <cstdio>
      4 #include <cstring>
      5 #include <algorithm>
      6 #define N1 105000
      7 #define S1 (N1<<1)
      8 #define T1 (N1<<2)
      9 #define M1 105000
     10 #define ll long long
     11 #define uint unsigned int
     12 #define rint register int 
     13 #define dd double
     14 #define il inline 
     15 #define inf 0x3f3f3f3f
     16 #define idx(X) (X-'0')
     17 using namespace std;
     18 
     19 int gint()
     20 {
     21     int ret=0,fh=1;char c=getchar();
     22     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
     23     while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
     24     return ret*fh;
     25 }
     26 int n,m,len,de;
     27 char str[N1];
     28 struct Edge{
     29 int to[S1],nxt[S1],head[S1],cte;
     30 void ae(int u,int v){
     31     cte++;to[cte]=v,nxt[cte]=head[u],head[u]=cte;}
     32 }E;
     33 namespace seg{
     34 int ls[S1*30],rs[S1*30],root[S1],tot;
     35 ll sum[S1*30];
     36 int merge(int rt1,int rt2)
     37 {
     38     if(!rt1||!rt2) return rt1+rt2;
     39     int nx=++tot;
     40     sum[nx]=sum[rt1]+sum[rt2];
     41     ls[nx]=merge(ls[rt1],ls[rt2]);
     42     rs[nx]=merge(rs[rt1],rs[rt2]);
     43     return nx;
     44 }
     45 void update(int x,int l,int r,int &rt)
     46 {
     47     if(!rt) rt=++tot;
     48     sum[rt]=1;
     49     if(l==r) return;
     50     int mid=(l+r)>>1;
     51     if(x<=mid) update(x,l,mid,ls[rt]);
     52     else update(x,mid+1,r,rs[rt]);
     53     //pushup(rt);
     54 }
     55 ll query(int L,int R,int l,int r,int rt)
     56 {
     57     if(!rt) return 0;
     58     if(L<=l&&r<=R) return sum[rt];
     59     int mid=(l+r)>>1;ll ans=0;
     60     if(L<=mid) ans+=query(L,R,l,mid,ls[rt]);
     61     if(R>mid) ans+=query(L,R,mid+1,r,rs[rt]);
     62     return ans;
     63 }
     64 };
     65 namespace SAM{
     66 int trs[S1][10],pre[S1],dep[S1],ed[S1],mi[S1],la,tot;
     67 void init(){tot=la=1;}
     68 void insert(int c,int id)
     69 {
     70     int p=la,np=++tot,q,nq;la=np;
     71     dep[np]=dep[p]+1;ed[np]=id;
     72     for(;p&&!trs[p][c];p=pre[p]) trs[p][c]=np;
     73     seg::update(id,1,n,seg::root[np]);
     74     if(!p){pre[np]=1;return;}
     75     q=trs[p][c];
     76     if(dep[q]==dep[p]+1) pre[np]=q;
     77     else{
     78         pre[nq=++tot]=pre[q];
     79         pre[q]=pre[np]=nq;
     80         dep[nq]=dep[p]+1;
     81         memcpy(trs[nq],trs[q],sizeof(trs[q]));
     82         for(;p&&trs[p][c]==q;p=pre[p]) trs[p][c]=nq;
     83     }
     84 }
     85 int hs[S1],que[S1],edq[S1],k,l,tt;
     86 void build()
     87 {
     88     //memset(mi,0x3f,sizeof(mi));
     89     for(int i=2;i<=tot;i++) mi[i]=n+1;
     90     for(int i=2;i<=tot;i++) hs[dep[i]]++;
     91     for(int i=1;i<=n;i++) hs[i]+=hs[i-1];
     92     for(int i=2;i<=tot;i++) que[hs[dep[i]]--]=i;
     93     for(int i=tot-1,x;i>=1;i--)
     94     {
     95         x=que[i],E.ae(pre[x],x);
     96         if(ed[x]) mi[x]=min(mi[x],ed[x]);
     97         mi[pre[x]]=min(mi[pre[x]],mi[x]);
     98         seg::root[pre[x]]=seg::merge(seg::root[pre[x]],seg::root[x]);
     99     }
    100 }
    101 void find(int L,int &F)
    102 {
    103     int x=1,c,fl=0;
    104     for(int i=1;i<=L;i++)
    105     {
    106         c=idx(str[i]);
    107         //for(;x&&!trs[x][c];x=pre[x]);
    108         if(!trs[x][c]) return;
    109         x=trs[x][c];
    110     }
    111     F=mi[x];
    112 }
    113 };
    114 
    115 int main()
    116 {
    117     scanf("%d",&n);
    118     scanf("%s",str+1);
    119     SAM::init();
    120     for(int i=1;i<=n;i++) 
    121         SAM::insert(idx(str[i]),i);
    122     SAM::build(); 
    123     scanf("%d",&m);
    124     int F,c,x;
    125     for(int i=1;i<=m;i++)
    126     {
    127         scanf("%s",str+1);
    128         len=strlen(str+1);
    129         F=n+1;
    130         SAM::find(len,F);
    131         ll ans=0;
    132         if(F!=n+1) ans=F-len;
    133         else ans=n;
    134         x=1;
    135         for(int j=1;j<=len;j++)
    136         {
    137             c=idx(str[j]);
    138             //for(;x&&!SAM::trs[x][c];x=SAM::pre[x]);
    139             x=SAM::trs[x][c];
    140             if(!x) break;
    141             ans+=seg::query(1,(F==n+1?n:F-len+j),1,n,seg::root[x]);
    142         }
    143         printf("%lld
    ",ans);
    144     }
    145     return 0;
    146 }
  • 相关阅读:
    Coursera机器学习week11 单元测试
    关于 TypeReference 的解释
    getModifiers 方法解释。
    instanceof isInstance isAssignableFrom 比较
    elasticsearch 基础 语法总结
    kibana 启动 关闭 和进程查找
    MD5 SHA1 SHA256 SHA512 SHA1WithRSA 的区别
    spring boot 项目 热启动
    java zip 压缩文件
    Packet for query is too large (1660 > 1024). You can change this value on the server by setting the max_allowed_packet' variable.
  • 原文地址:https://www.cnblogs.com/guapisolo/p/10116034.html
Copyright © 2011-2022 走看看