[BZOJ3413]匹配

bzoj

description

sol

首先你要求出\(s_i\)第一次匹配的位置。(或者是没有匹配位置)
然后问题就大致转化为了:求\(s_i\)和原串中第一次匹配位置之前的所有后缀的\(lcp\)之和。(有一些小细节就自己\(yy\)一下吧)
这个问题显得不是那么好做,考虑把这个问题转化一下,改为求\(s_i\)的每个前缀在匹配位置之前的出现次数。
然后看上去就很简单了?对\(SAM\)线段树合并\(endpos\)集合,然后一边跑匹配一边线段树区间查询。复杂度\(O(\sum len\times\log n)\)

code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
const int N = 2e5+5;
struct seg{int ls,rs,sz;}t[N*40];
int n,q,tot=1,last=1,tr[N][10],fa[N],len[N],rt[N],Node,tong[N],a[N];
char s[N];
void extend(int c){
	int v=last,u=++tot;last=u;
	len[u]=len[v]+1;
	while (v&&!tr[v][c]) tr[v][c]=u,v=fa[v];
	if (!v) fa[u]=1;
	else{
		int x=tr[v][c];
		if (len[x]==len[v]+1) fa[u]=x;
		else{
			int y=++tot;
			memcpy(tr[y],tr[x],sizeof(tr[y]));
			fa[y]=fa[x];fa[x]=fa[u]=y;len[y]=len[v]+1;
			while (v&&tr[v][c]==x) tr[v][c]=y,v=fa[v];
		}
	}
}
void modify(int &x,int l,int r,int p){
	if (!x) x=++Node;++t[x].sz;
	if (l==r) return;int mid=l+r>>1;
	if (p<=mid) modify(t[x].ls,l,mid,p);
	else modify(t[x].rs,mid+1,r,p);
}
int merge(int x,int y){
	if (!x||!y) return x|y;
	int z=++Node;t[z].sz=t[x].sz+t[y].sz;
	t[z].ls=merge(t[x].ls,t[y].ls);
	t[z].rs=merge(t[x].rs,t[y].rs);
	return z;
}
int find(int x,int l,int r){
	if (l==r) return l;int mid=l+r>>1;
	if (t[t[x].ls].sz) return find(t[x].ls,l,mid);
	else return find(t[x].rs,mid+1,r);
}
int query(int x,int l,int r,int ql,int qr){
	if (l>=ql&&r<=qr) return t[x].sz;
	int mid=l+r>>1,res=0;
	if (ql<=mid) res+=query(t[x].ls,l,mid,ql,qr);
	if (qr>mid) res+=query(t[x].rs,mid+1,r,ql,qr);
	return res;
}
int main(){
	n=gi();scanf("%s",s+1);
	for (int i=1;i<=n;++i) extend(s[i]-'0'),modify(rt[last],1,n,i);
	for (int i=1;i<=tot;++i) ++tong[len[i]];
	for (int i=1;i<=n;++i) tong[i]+=tong[i-1];
	for (int i=1;i<=tot;++i) a[tong[len[i]]--]=i;
	for (int i=tot;i>1;--i) rt[fa[a[i]]]=merge(rt[fa[a[i]]],rt[a[i]]);
	q=gi();while (q--){
		scanf("%s",s+1);int l=strlen(s+1),now=1,lst;
		long long ans=0;
		for (int i=1;i<=l;++i) now=tr[now][s[i]-'0'];
		if (now) lst=find(rt[now],1,n),ans=lst-l;
		else lst=-1,ans=n;
		now=1;
		for (int i=1;i<=l;++i){
			now=tr[now][s[i]-'0'];
			if (now) ans+=query(rt[now],1,n,1,lst==-1?n:lst-l+i);
		}
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2018-06-19 20:40  租酥雨  阅读(263)  评论(0编辑  收藏  举报