分析:
注意到题目要求单词不重叠恰好覆盖文章,所以不能直接上AC自动机。
这里的思路有点像可行性DP,f[i]表示是/否可以用一些单词恰好覆盖前i个字符,显然f[i]=f[i] or f[i-一个可行的单词],这个DP过程可以用AC自动机优化。
具体见代码。
(AC自动机的根的编号设为0更好写,好长时间没写AC自动机了这道题调了好长时间 。)
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <ctime>
//#include <unistd.h>
int n,m,tot=0,tr[205][26],fail[205],l[205];
char s[1100005];
bool ed[205],f[1100005];
inline void ins(char* str){
int now=0,len=strlen(str+1);
for(int i=1;i<=len;i++){
if(!tr[now][str[i]-'a']) tr[now][str[i]-'a']=++tot;
now=tr[now][str[i]-'a'];
}
ed[now]=1;
l[now]=len;
}
std::queue<int> q;
inline void getfail(){
while(!q.empty()) q.pop();
for(int i=0;i<26;i++){
if(!tr[0][i]) continue;
fail[tr[0][i]]=0;
q.push(tr[0][i]);
}
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<26;i++){
if(tr[u][i]){
fail[tr[u][i]]=tr[fail[u]][i];
q.push(tr[u][i]);
}
else tr[u][i]=tr[fail[u]][i];
}
ed[u]|=ed[fail[u]];
}
}
int query(char* str){
int now=0,len=strlen(str+1);
for(int i=1;i<=len;i++){
now=tr[now][str[i]-'a'];
if(ed[now]){
int temp=now;
while(temp){
if(l[temp]) f[i]|=f[i-l[temp]];
temp=fail[temp];
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
getchar();
scanf("%s",s+1);
ins(s);
}
getfail();
for(int i=1;i<=m;i++){
getchar();
scanf("%s",s+1);
memset(f,0,sizeof f);
f[0]=1;
query(s);
int len=strlen(s+1);
for(int i=len;i+1;i--){
if(f[i]){
printf("%d\n",i);
break;
}
}
}
return 0;
}