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 }