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;
}
}
}