LGP2292题解

神奇题目(

首先观察一下,有一个简单的 \(O(nm|t|)\) 的暴力,即对字符串的每一个前缀记录哪些前缀能够到达这个前缀。

观察到 \(s\) 的长度只有 \(20\),这给了我们机会。

看到 \(20\),你要想的东西不止有状压,还可能是 bitset。

对于一个前缀,记录 \(p[i]\) 表示这个前缀再接上一个串可能到达哪些前缀。具体地,若 \(p[i]\) 在二进制下的第 \(j\) 位为 \(1\),那么我们就认为 \(s(i,i+j]\) 这个前缀是能够被某个位置匹配得上的。

我们再记录一个 \(f[i]\),表示从前面的某个位置接上一个串能够到达哪些位置。具体地,若 \(f[i]\) 在二进制下的第 \(j\) 位为 \(1\),那么 \(s[i+j]\) 这个前缀一定是合法的。

有了 \(p\) 我们可以很方便地转移 \(f\),只需要判断 \(f[i]\) 在二进制下的第 \(0\) 为即可。

最后一个问题,如何得到 \(p\)

对这些串建一个 ACAM,然后对每个节点记录在 fail 树到根节点的链上的终止节点对应的长度就可以了。

#include<cstring>
#include<cstdio>
#include<cctype>
typedef unsigned ui;
const ui N=405,M=2e6+5;
ui n,m,tot(1),t[N],fail[N],trans[N][26];ui p[M],f[M];char s[M];
inline void Build(){
	static ui L,R,q[M];L=1;
	for(ui c=0;c<26;++c){
		if(trans[1][c])q[++R]=trans[1][c],fail[trans[1][c]]=1;
		else trans[1][c]=1;
	}
	while(L<=R){
		const ui&u=q[L++];
		for(ui c=0;c<26;++c){
			if(trans[u][c])q[++R]=trans[u][c],fail[trans[u][c]]=trans[fail[u]][c];
			else trans[u][c]=trans[fail[u]][c];
		}
	}
	for(ui i=1;i<=R;++i)t[q[i]]|=t[fail[q[i]]];
}
signed main(){
	scanf("%u%u",&n,&m);
	for(ui i=1;i<=n;++i){
		scanf("%s",s+1);
		ui u(1),n=strlen(s+1);
		for(ui i=n;i>=1;--i){
			const ui&c=s[i]-97;
			if(!trans[u][c])trans[u][c]=++tot;
			u=trans[u][c];
		}
		for(ui i=1;i<=n;++i)s[i]=0;
		t[u]|=1<<n-1;
	}
	Build();
	while(m--){
		scanf("%s",s+1);n=strlen(s+1);f[0]=1;
		for(ui u(1),i=n;i>=1;--i)p[i]=t[u=trans[u][s[i]-97]];
		for(ui i=1;i<=n;++i)f[i]=f[i-1]>>1|(f[i-1]&1?p[i]:0);
		for(ui i=1;i<=n;++i)s[i]=0;
		for(ui i=n;i>=0;--i)if(f[i]&1){
			printf("%u\n",i);break;
		}
	}
}
posted @ 2022-03-18 19:15  Prean  阅读(23)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};