首页 > 技术文章 > P2292 [HNOI2004]L语言

y2823774827y 2019-01-21 10:09 原文

题目

P2292 [HNOI2004]L语言

化简题目:多个匹配串,多个文本串,求文本串恰好被匹配串拼接的最长前缀

做法

显然我们对文本串建AC自动机,然后标记末节点

\(tire\)图,需要注意的是:末节点标记不能下传,原因在于对文本串跑差分的操作,自己想想吧

多于每个文本串跑\(trie\)图,对每个字符到达一个点后沿着\(fail\)往上跑,如果是末节点,说明我们匹配到一个串了,然后做差分就好了

My complete code

#include<cstdio>
#include<cstring>
#include<iostream>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
typedef int LL;
const LL maxn=1e6;
LL nod=1;
LL son[maxn][26],fail[maxn],dep[maxn];
bool falg[maxn];
inline void Insert(char *s){
	LL Len=strlen(s+1),now=0;
	for(LL i=1;i<=Len;++i){
		LL c=s[i]-'a';
		if(son[now][c]==0){
		    son[now][c]=++nod;
		    //dep[nod]=dep[now]+1;
		}
		now=son[now][c];
		dep[now]=i;
	}falg[now]=true;
}
LL n,T;
LL cf[maxn];
bool visit[maxn];
char s[maxn];
inline void F_fail(){
	queue<LL>que;
	for(LL i=0;i<26;++i)
	    if(son[0][i])
	        que.push(son[0][i]);
	while(que.size()){
		LL u(que.front()); que.pop();
		for(LL i=0;i<26;++i){
			LL v(son[u][i]);
			if(v){
				fail[v]=son[fail[u]][i],
				que.push(v);
			}else
			    son[u][i]=son[fail[u]][i];
		}
	}
}
int main(){
	scanf("%d%d",&n,&T);
	for(LL i=1;i<=n;++i){
		scanf(" %s",s+1);
		Insert(s);
	}
	F_fail();
	while(T--){
		scanf(" %s",s+1);
		LL now(0),Len=strlen(s+1);
		memset(visit,false,sizeof(visit));memset(cf,0,sizeof(cf));
		visit[0]=true;
		for(LL i=1;i<=Len;++i){
			LL c=s[i]-'a';
			now=son[now][c];
			for(LL u=now;u;u=fail[u])
			    if(falg[u]&&visit[i-dep[u]]){
				    visit[i]=true,
				    ++cf[i-dep[u]+1],
				    --cf[i+1];
			    }
		}now=0; LL ans(0);
		for(LL i=1;i<=Len;++i){
			now+=cf[i];
			if(now==0)
			    break;
			else
			    ++ans;
		}
		printf("%d\n",ans);
	}
}/*
*/

推荐阅读