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 }
  • 相关阅读:
    配置webpack.config.js中的文件
    webpack-dev-server运行时报错
    vue 中的通过搜索框进行数据过滤的过程
    vue中的computed(计算属性)和watch(监听属性)的特点,以及深度监听
    关于vue-router 中参数传递的那些坑(params,query)
    treeSet比较功能
    练习:往HashSet中存储学生对象(姓名,年龄) 同姓名,同年龄视为一个人,不存
    LinkedList实现堆栈或者队列
    LInkedList特有方法演示
    ★★★ArrayList去除指定对象的重复元素
  • 原文地址:https://www.cnblogs.com/guapisolo/p/10116034.html
Copyright © 2011-2022 走看看