BZOJ3413: 匹配

BZOJ3413: 匹配


题目描述

传送门

题目分析

我们设询问串为\(t\)从头开始的第一个字符串为\(s[1]\),设在位置\(x\)匹配成功,则题目要求的就是\(\sum_{i=1}^xlcp(s[i],t)\)

可以考虑先对\(s\)建立后缀自动机,然后考虑每一个\(endpos\)类会对答案造成多少贡献。

明显可以发现匹配位置之后的都对答案没有贡献,所以可以使用线段树合并维护\(right\)集合,查询的时候直接查匹配位置前面的部分即可。

是代码呢

#include <bits/stdc++.h>
using namespace std;
const int MAXN=2e5+7;
#define mid ((l+r)>>1)
int n,m,T[MAXN],st[MAXN<<5],L[MAXN<<5],R[MAXN<<5],sz;
char s[MAXN];
inline void modify(int &u,int l,int r,int k)
{
	if(!u) u=++sz;
	st[u]++;
	if(l==r) return;
	if(mid>=k) modify(L[u],l,mid,k);
	else modify(R[u],mid+1,r,k);
}
inline int query(int u,int l,int r,int dl,int dr)
{
	if(dl<=l&&r<=dr) return st[u];
	int ans=0;
	if(dl<=mid) ans+=query(L[u],l,mid,dl,dr);
	if(dr>mid) ans+=query(R[u],mid+1,r,dl,dr);
	return ans;
}
inline int merge(int u,int v)
{
	if(!u||!v) return u+v;
	int rt=++sz;
	st[rt]=st[u]+st[v];
	L[rt]=merge(L[u],L[v]);R[rt]=merge(R[u],R[v]);
	return rt;
}
struct SAM{
	int trans[MAXN][27],maxlen[MAXN],fa[MAXN],od[MAXN],pos[MAXN],wt[MAXN];
	int cnt,last,p,np,q,nq;
	SAM(){last=++cnt;}
	inline void insert(int x,int id){
		p=last;last=np=++cnt;maxlen[np]=maxlen[p]+1;pos[np]=id;
		modify(T[np],1,n,id);
		while(!trans[p][x]&&p) trans[p][x]=np,p=fa[p];
		if(!p) fa[np]=1;
		else {
			q=trans[p][x];
			if(maxlen[q]==maxlen[p]+1) fa[np]=q;
			else {
				nq=++cnt;maxlen[nq]=maxlen[p]+1;
				pos[nq]=pos[q];
				memcpy(trans[nq],trans[q],sizeof(trans[q]));
				fa[nq]=fa[q];fa[q]=fa[np]=nq;
				while(trans[p][x]==q) trans[p][x]=nq,p=fa[p];
			}
		}
	}
	inline void get_right(){
		for(int i=1;i<=cnt;i++) wt[maxlen[i]]++;
		for(int i=1;i<=n;i++) wt[i]+=wt[i-1];
		for(int i=cnt;i;i--) od[wt[maxlen[i]]--]=i;
		for(int i=cnt;i>1;i--) T[fa[od[i]]]=merge(T[fa[od[i]]],T[od[i]]),pos[fa[od[i]]]=min(pos[fa[od[i]]],pos[od[i]]);
	}
	inline int check(){
		int now=1;
		for(int i=1,l=strlen(s+1);i<=l;i++){
			if(trans[now][s[i]-'0']) now=trans[now][s[i]-'0'];
			else return -1;
		}
		return pos[now];
	}
}sam;
int main()
{
	cin>>n;
	scanf("%s",s+1);
	for(int i=1;i<=n;i++) sam.insert(s[i]-'0',i);
	sam.get_right();
	cin>>m;
	while(m--){
		int ans=0;
		scanf("%s",s+1);
		int l=strlen(s+1),h=sam.check();
		if(h==-1) ans=n;
		else ans=h+1-l;
		for(int i=1,now=1;i<l;i++){
			if(sam.trans[now][s[i]-'0']) now=sam.trans[now][s[i]-'0'];
			else break;
			if(h==-1) ans+=query(T[now],1,n,1,n);
			else ans+=query(T[now],1,n,1,h-l+i);
		}
		printf("%d\n",ans);
	}
}
posted @ 2019-03-22 07:58  ~victorique~  阅读(350)  评论(0编辑  收藏  举报
Live2D