BZOJ3413: 匹配

题面:https://www.lydsy.com/JudgeOnline/problem.php?id=3413
题解:
首先考虑匹配次数的意义。可以看出匹配的过程就是拿\(A\)串的所有前缀
\(B\)串一一匹配。考虑\(A\)串的每一位。设当前位一共被匹配了\(f[i]\)次,
那么答案即为\(\sum\) \(f[i]\)
考虑\(f[i]\)的组成。它包括了两个部分:匹配成功的次数和失配次数。
其中,失配次数是可以单独计算的。
我们可以给\(A\)串建立SAM,用\(B\)串在上面跑。
如果跑到了0号节点,说明整个匹配过程是失败的,所以失配次数为\(n\)
否则,失配次数就是跑到的那个节点的endpos最靠左的那个节点。
至于如何记这个endpos,可以用线段树合并。具体实现就是先对所有SAM上
的节点拓扑排序,然后从大到小合并上去即可。
现在考虑匹配成功的次数。考虑\(B\)串在\(A\)串的SAM上跑的过程。
每次跑到一个非根节点,这个节点的线段树上存有当前位置所有的endpos。
设失配次数为\(x\),那么当前节点能计入贡献的只有endpos<=\(x\)的那些
(注意这里的endpos代表的是一个前缀的一个子串),其他的那些因为已经匹配完了,
所以不计入贡献。
时间复杂度:\(O(SlogS)\)\(S\)指的是\(B\)串总长)
代码:

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
typedef long long ll;
#define I inline void
#define IN inline int
#define C(x,y) memset(x,y,sizeof(x))
#define STS system("pause")
template<class D>I read(D &res){
	res=0;register D g=1;register char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')g=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		res=(res<<3)+(res<<1)+(ch^48);
		ch=getchar();
	}
	res*=g;
}
const int INF=1e9+7;
char c[101000];
int n,m,leng,sum,root[202000],lc[4040000],rc[4040000],w[4040000];
I modi(int &k,int l,int r,int x){
	if(!k)k=++sum;w[k]++;
	if(l==r)return;
	re mid=(l+r)>>1;
	if(x<=mid)modi(lc[k],l,mid,x);
	else modi(rc[k],mid+1,r,x);
}
IN merge(int x,int y){
	if(!x&&!y)return 0;
	if(!x)return y;
	if(!y)return x;
	re now=++sum;
	lc[now]=merge(lc[x],lc[y]);rc[now]=merge(rc[x],rc[y]);w[now]=w[x]+w[y];
	return now;
}
IN ques_min(int k,int l,int r){
	if(l==r)return l;
	re mid=(l+r)>>1;
	if(w[lc[k]])return ques_min(lc[k],l,mid);
	else return ques_min(rc[k],mid+1,r);
}
inline ll ques_sum(int k,int l,int r,int lim){
	if(!k||l>lim)return 0;
	if(l==r||r<=lim)return w[k];
	re mid=(l+r)>>1;
	return ques_sum(lc[k],l,mid,lim)+ques_sum(rc[k],mid+1,r,lim);
}
namespace SAM{
	ll ans;
	int len[202000],link[202000],ch[202000][10],buc[202000],sa[202000],p,q,las,cur,tot,cle;
	I add(int x){
		len[cur=++tot]=len[las]+1,p=las,las=cur;
		while(p&&!ch[p][x])ch[p][x]=cur,p=link[p];
		if(!p){link[cur]=1;return;}
		q=ch[p][x];
		if(len[p]+1==len[q]){link[cur]=q;return;}
		cle=++tot;len[cle]=len[p]+1,link[cle]=link[q];
		memcpy(ch[cle],ch[q],sizeof(ch[q]));
		while(p&&ch[p][x]==q)ch[p][x]=cle,p=link[p];
		link[q]=link[cur]=cle;
	}
	I init(){
		tot=las=1;
		F(i,1,n)add(c[i]-'0'),modi(root[cur],1,n,i);
		F(i,1,tot)buc[len[i]]++;
		F(i,1,tot)buc[i]+=buc[i-1];
		FOR(i,tot,1)sa[buc[len[i]]--]=i;
		FOR(i,tot,1)if(sa[i]!=1)root[link[sa[i]]]=merge(root[link[sa[i]]],root[sa[i]]);
	}
	I ques(){
		p=1;q=INF;
		F(i,1,leng)p=ch[p][c[i]-'0'];
		if(!p)ans=n;
		else{
			q=ques_min(root[p],1,n);
			ans=q-leng;
		}
		p=1;
		F(i,1,leng){
			p=ch[p][c[i]-'0'];
			if(!p)break;
			ans+=ques_sum(root[p],1,n,q+i-leng);
		}
		printf("%lld\n",ans);
	}
}
int main(){
	read(n);
	scanf("%s",c+1);
	SAM::init();
	read(m);
	while(m--){
		scanf("%s",c+1);
		leng=strlen(c+1);
		SAM::ques();
	}
	return 0;
}
posted @ 2020-01-09 11:29  Purple_wzy  阅读(135)  评论(0编辑  收藏  举报