bzoj 3413: 匹配 后缀自动机

题意

在这里插入图片描述
n ≤ 100000 n\le100000 n100000

分析

题目相当于求询问串与母串的第一个匹配位置之前所有后缀的lcp的和。
对母串建sam,把每个节点的right集用线段树合并求出来,然后把询问串在上面跑顺便统计答案就好了。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>

typedef long long LL;

const int N=100005;

int n,m,sz,tot,fa[N*2],ch[N*2][10],mx[N*2],last,rt[N*2],b[N*2],c[N*2],pos[N];
char str[N];
struct tree{int l,r,s;}t[N*40];

void extend(int x)
{
	int p,q,np,nq;
	p=last;last=np=++tot;mx[np]=mx[p]+1;
	for (;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;
	if (!p) fa[np]=1;
	else
	{
		q=ch[p][x];
		if (mx[q]==mx[p]+1) fa[np]=q;
		else
		{
			nq=++tot;mx[nq]=mx[p]+1;
			memcpy(ch[nq],ch[q],sizeof(ch[q]));
			fa[nq]=fa[q];fa[q]=fa[np]=nq;
			for (;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
		}
	}
}

void ins(int &d,int l,int r,int x)
{
	if (!d) d=++sz;
	t[d].s++;
	if (l==r) return;
	int mid=(l+r)/2;
	if (x<=mid) ins(t[d].l,l,mid,x);
	else ins(t[d].r,mid+1,r,x);
}

int merge(int x,int y)
{
	if (!x||!y) return x^y;
	int p=++sz;
	t[p].l=merge(t[x].l,t[y].l);
	t[p].r=merge(t[x].r,t[y].r);
	t[p].s=t[x].s+t[y].s;
	return p;
}

int get_fir(int d,int l,int r)
{
	if (l==r) return l;
	int mid=(l+r)/2;
	if (t[t[d].l].s) return get_fir(t[d].l,l,mid);
	else return get_fir(t[d].r,mid+1,r);
}

int query(int d,int l,int r,int x,int y)
{
	if (!d||x<=l&&r<=y) return t[d].s;
	int mid=(l+r)/2;
	if (y<=mid) return query(t[d].l,l,mid,x,y);
	else if (x>mid) return query(t[d].r,mid+1,r,x,y);
	else return query(t[d].l,l,mid,x,y)+query(t[d].r,mid+1,r,x,y);
}

void pre()
{
	for (int i=1;i<=tot;i++) b[mx[i]]++;
	for (int i=1;i<=n;i++) b[i]+=b[i-1];
	for (int i=1;i<=tot;i++) c[b[mx[i]]--]=i;
	for (int i=1;i<=n;i++) ins(rt[pos[i]],1,n,i);
	for (int i=tot;i>1;i--) rt[fa[c[i]]]=merge(rt[fa[c[i]]],rt[c[i]]);
}

int main()
{
	scanf("%d%s",&n,str+1);
	last=tot=1;
	for (int i=1;i<=n;i++) extend(str[i]-'0'),pos[i]=last;
	pre();
	scanf("%d",&m);
	while (m--)
	{
		scanf("%s",str+1);
		int len=strlen(str+1),x=1;
		LL ans=0;
		bool flag=0;
		for (int i=1;i<=len;i++) x=ch[x][str[i]-'0'];
		if (!x)
		{
			x=1;
			for (int i=1;i<=len;i++) x=ch[x][str[i]-'0'],ans+=t[rt[x]].s;
			printf("%lld\n",ans+n);
		}
		else
		{
			int p=get_fir(rt[x],1,n)-len+1;
			x=1;
			for (int i=1;i<=len;i++) x=ch[x][str[i]-'0'],ans+=query(rt[x],1,n,1,p+i-1);
			printf("%lld\n",ans+p-1);
		}
	}
	return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值