P2292 [HNOI2004]L语言
题面传送门
数据这么大显然是要我们用线性算法。
考虑把这个东西放到AC自动机上,预处理出每个节点能从哪些位置转移,这样的复杂度是\(O(m|s||t|+n|s|)\)的,不是很优秀,可能会被卡满。
发现dp转移方程是一个或的形式,也就是只要有一个为\(1\)即可而并不要求是哪一个为\(1\),所以就可以考虑状压,在AC自动机上状压前\(10\)个哪些可以转移,转移时维护一个dp数组上的前\(10\)个转移状态,与一下即可。
时间复杂度\(O(m|t|+n|s|)\)就可以过了。
代码实现:
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
int n,m,k,x,y,z,cnt,dp[2000039],ans[239],flag[239],pus,now,tot;
char s[139],t[2000039];
struct AC{int son[26],fail;}f[239];
inline void get(){
register int i;now=0;
for(i=1;i<=k;i++) now=f[now].son[s[i]-'a']?f[now].son[s[i]-'a']:(f[now].son[s[i]-'a']=++cnt);ans[now]|=1<<k-1;
}
queue<int> q;
inline void bfs(){
register int i;
for(i=0;i<=25;i++) if(f[0].son[i]) q.push(f[0].son[i]);
while(!q.empty()){
now=q.front();q.pop();
for(i=0;i<=25;i++){
if(f[now].son[i]) f[f[now].son[i]].fail=f[f[now].fail].son[i],q.push(f[now].son[i]);
else f[now].son[i]=f[f[now].fail].son[i];
}
}
}
inline void dfs(int x){
if(!x||flag[x]) return;flag[x]=1;
dfs(f[x].fail);ans[x]|=ans[f[x].fail];
}
int main(){
// freopen("1.in","r",stdin);
register int i,j;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)scanf("%s",s+1),k=strlen(s+1),get();bfs();
for(i=1;i<=cnt;i++) dfs(i);
for(i=1;i<=m;i++){
for(j=1;j<=k;j++) dp[j]=0;dp[0]=1;now=0;pus=1;
scanf("%s",t+1);k=strlen(t+1);tot=0;
for(j=1;j<=k;j++){
now=f[now].son[t[j]-'a'];
dp[j]=(pus&ans[now])?1:0;
if(dp[j]) tot=j;
pus=(pus<<1|dp[j])&1023;
}
printf("%d\n",tot);
}
}