【BZOJ2780】【Spoj8093】 Sevenk Love Oimaster 后缀自动机

#include <stdio.h>
int main()
{
	puts("转载请注明出处谢谢");
	puts("http://blog.csdn.net/vmurder/article/details/43015849");
}

题意:

n,m

n个串

m个串

样例里面倒数第二行的you应该扔到下一行。


问m个串每个在前n个串中的几个出现过。


题解:

首先这道题跟

【BZOJ2754】【SCOI2012】喵星球上的点名

是一样的,只不过更卡时一点,或者说喵的数据太弱。


这道题虽然是后缀自动机,但是大体思路和

【BZOJ2434】【NOI2011】阿狸的打字机 AC自动机

是一样的。都是dfs序+树状数组优化


好了,说这道题。

就是我们建广义后缀树【就是多串的反序后缀自动机】

然后根据dfs序快速转移,用树状数组维护某区间有多少个不同的串。

我来贴两个详细的 题解 网址

16bitwar:http://blog.csdn.net/jiangyuze831/article/details/42964105

wyfcyx:http://wyfcyx.is-programmer.com/posts/76391.html

代码:

#include <map>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 201000
#define T 26
using namespace std;

struct Suffix_Tree // 广义后缀树
{
	int head[N],next[N],v[N],cnt;
	void add(int u,int _v)
	{
		v[++cnt]=_v;
		next[cnt]=head[u];
		head[u]=cnt;
	}
	int in[N],out[N],dfn,stk[N];
	void build_dfn(int x)
	{
		in[x]=++dfn;
		stk[dfn]=x;
		for(int i=head[x];i;i=next[i])
			build_dfn(v[i]);
		out[x]=dfn;
	}
}sft,belong;

struct QUERY
{
	int id,p;
	QUERY(int _id=0,int _p=0):id(_id),p(_p){}
	bool operator < (const QUERY &a)const
		{return sft.out[p]<sft.out[a.p];}
}ask[N];
int vis[N],ans[N];
char s[N];
int n,m;

struct FENWICK
{
	int a[N],cnt[N];
	void add(int p,int x){for(;p<N;p+=p&-p)a[p]+=x;}
	int query(int p)
	{
		int ans=0,temp=p;
		for(p=sft.out[temp] ;p;p-=p&-p)ans+=a[p];
		for(p=sft.in[temp]-1;p;p-=p&-p)ans-=a[p];
		return ans;
	}
}fw;

struct SAM // 广义后缀自动机
{
	map<int,int>next[N];
	int pa[N],deep[N];
	int root,last,cnt;
	int newnode(int dep){deep[++cnt]=dep;return cnt;}
	void init(){root=last=cnt=1;}
	void add(char alp,int id) // 建立广义后缀自动机
	{
		int np=next[last][alp];
		if(np)
		{
			if(deep[np]==deep[last]+1)last=np;
			else {
				int nq=newnode(deep[last]+1);
				pa[nq]=pa[np],pa[np]=nq;
				next[nq]=next[np];
				int p=last;
				while(p&&next[p][alp]==np)
					next[p][alp]=nq,p=pa[p];
				last=nq;
			}
		}
		else {
			np=newnode(deep[last]+1);
			int p=last;
			while(p&&!next[p][alp])next[p][alp]=np,p=pa[p];
			if(!p)pa[np]=root;
			else {
				int q=next[p][alp];
				if(deep[q]==deep[p]+1)pa[np]=q;
				else {
					int nq=newnode(deep[p]+1);
					pa[nq]=pa[q],pa[q]=pa[np]=nq;
					next[nq]=next[q];
					while(p&&next[p][alp]==q)
						next[p][alp]=nq,p=pa[p];
				}
			}
			last=np;
		}
		belong.add(last,id);
	}
	void build_sft(){for(int i=2;i<=cnt;i++)sft.add(pa[i],i);}
	void deal_query()
	{
		bool find;
		int i,j,p;
		for(int i=1;i<=m;i++)
		{
			scanf("%s",s),find=1;
			for(p=root,j=0;s[j];j++)
			{
				if(!next[p][s[j]]){find=0;break;}
				p=next[p][s[j]];
			}
			if(find)ask[i]=QUERY(i,p);
			else ask[i]=QUERY(i,-1);
		}
		sort(ask+1,ask+m+1);
	}
}sam;

int main()
{
//	freopen("test.in","r",stdin);
	int i,j,k;
	scanf("%d%d",&n,&m);
	sam.init();
	for(i=1;i<=n;i++)
	{
		scanf("%s",s);
		sam.last=sam.root;
		for(j=0;s[j];j++)sam.add(s[j],i);
	}
	sam.build_sft();
	sft.build_dfn(1);
	sam.deal_query();
	
	for(k=1;k<=m&&ask[k].p==-1;k++);
	for(j=1;j<=sam.cnt;j++)
	{
		int u=sft.stk[j];
		for(i=belong.head[u];i;i=belong.next[i])
		{
			int v=belong.v[i];
			fw.add(j,1);
			if(vis[v])fw.add(vis[v],-1);
			vis[v]=j;
		}
		while(sft.out[ask[k].p]==j)
		{
			ans[ask[k].id]=fw.query(ask[k].p);
			k++;
		}
	}
	for(i=1;i<=m;i++)printf("%d\n",ans[i]);
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值