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\n",ans);
144     }
145     return 0;
146 }

 

posted @ 2018-12-13 19:55  guapisolo  阅读(428)  评论(0编辑  收藏  举报