ZOJ Searching the String(match指针暴力跳fail)

LINK

给出一个文本串 s s s n n n个模式串

问每个模式串 f i f_i fi在文本串 s s s中最多出现几次

t y p e = = 0 type==0 type==0表示可覆盖, t y p e = = 1 type==1 type==1表示不可覆盖

比如 a b a aba aba a b a b a ababa ababa中,若可覆盖则出现 2 2 2次,否则只出现一次


t y p e = = 0 type==0 type==0的模式串只需要跑一遍 a c ac ac自动机即可

t y p e = = 1 type==1 type==1的模式串,考虑第一次匹配到的时候可以直接让答案加一

第二次匹配到的时候如果和上次没有重叠答案也加一,这个只需要记录以下最后一次匹配位置即可

如果和上次有重合,直接丢弃这次的匹配结果即可,答案不加一.

a [ 1... l ] a[1...l] a[1...l]匹配到自动机位置 k k k,直接从 k k k往上暴力跳 f a i l fail fail即可

因为每个文本串的长度小于等于 6 6 6,所以只需要跳 6 6 6 m a t c h match match指针(只指向有单词节点)

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
char a[maxn],b[maxn];
int n,m,casenum;
typedef pair<int,int>p;
vector<p>vec[maxn];
int zi[maxn][30],id,len[maxn],type[maxn],ID[maxn],ok[maxn];
void insert(char s[],int is)
{
	int now = 0, le = strlen( s+1 );
	for(int i=1;i<=le;i++)
	{
		if( zi[now][s[i]-'a']==0 )	zi[now][s[i]-'a'] = ++id;
		now = zi[now][s[i]-'a'];
	}
	len[now] = le; ok[now] = 1; ID[is] = now;
}
int fail[maxn],ans[maxn][2],pre[maxn],match[maxn];
void make_fail()
{
	queue<int>q;
	for(int i=0;i<=25;i++)	if( zi[0][i] )	q.push( zi[0][i] );
	while( !q.empty() )
	{
		int u = q.front(); q.pop();
		for(int i=0;i<=25;i++)
		{
			int v = zi[u][i];
			if( v )
			{
				fail[v] = zi[fail[u]][i],q.push( v );	
				int temp = zi[fail[u]][i];
				if( ok[temp] )	match[v] = temp;
				else	match[v] = match[temp];
			}
			else
				zi[u][i] = zi[fail[u]][i];
		}
	}
}
void init()
{
	for(int i=0;i<=id;i++)
	{
		memset( zi[i],0,sizeof zi[i] );
		fail[i] = match[i] = ok[i] = len[i] = 0;
		pre[i] = ans[i][0] = ans[i][1] = 0;
		vec[i].clear();
	}
	id = 0;
	for(int i=0;i<=n;i++) ID[i] = 0;
}
int main()
{
	while( scanf("%s%d",a+1,&n)!=EOF )
	{
		for(int i=1;i<=n;i++)
		{		
			scanf("%d%s",&type[i],b+1 );
			insert( b,i );
		}
		make_fail();
		int now = 0;
		m = strlen( a+1 );
		for(int i=1;i<=m;i++)
		{
			now = zi[now][a[i]-'a'];
			int f = now;
			while( f!=0 )
			{
				ans[f][0]++;
				if( pre[f]+len[f]<=i )	ans[f][1]++, pre[f] = i;	
				f = match[f];
			}
		}
		printf("Case %d\n",++casenum);
		for(int i=1;i<=n;i++)	printf("%d\n",ans[ID[i]][type[i]] );
		init();
		puts("");
	}
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值