题目
字典里给出n(n<=20)个模式串,串长|s|<=10,
m(m<=50)个询问,每次询问一个文本串t(|t|<=2e6)的最长前缀长度,使前缀可以由字典里的串拼接而成
模式串和文本串均由小写字母组成
思路来源
https://www.luogu.com.cn/blog/fusu2333/solution-p2292
题解
很显然的做法是,暴力跳fail,如果fail是一个标记点,代表了一个长度为len的串,
则dp[i]|=dp[i-len],这样做,复杂度是O(m*|fail(s)|*|t|)的,不够优雅
于是注意到|s|只有10,状压10位,预处理fail树,
st[i]表示其fail树的祖先中,是否存在长度1-10的串,存在长度为i的则标记2的i次方这一位
而pre则表示,对于i来说,[i-10,i-1]这10个值中哪些位置dp=1,也用状压表示10位
如果二者有交,则dp[i]=1,这样就降到O(m*|t|)了
代码1(O(1)转移)
#include<bits/stdc++.h>
#define N 205
#define M 2000005
using namespace std;
bool dp[M];
namespace AC{
int nex[N][26],num[N],fail[N],q[N],h,t,c,id[N],st[N];
void init(){
c=0;
memset(nex[c],0,sizeof nex[c]);
fail[c]=num[c]=0;
id[c]=st[c]=0;
}
void ins(char *s,int n){
int rt=0;
for(int i=0;i<n;i++){
int v=s[i]-'a';
if(!nex[rt][v]){
nex[rt][v]=++c;
memset(nex[c],0,sizeof nex[c]);
fail[c]=num[c]=0;
id[c]=st[c]=0;
}
rt=nex[rt][v];
}
id[rt]=n;
}
void build(){
h=1;t=0;
for(int i=0;i<26;i++){
if(nex[0][i]){
fail[nex[0][i]]=0,q[++t]=nex[0][i];
}
}
while(h<=t){
int u=q[h++];
st[u]=st[fail[u]];
if(id[u])st[u]+=(1<<id[u]);
for(int i=0;i<26;i++){
if(nex[u][i])fail[nex[u][i]]=nex[fail[u]][i],q[++t]=nex[u][i];
else nex[u][i]=nex[fail[u]][i];
}
}
}
int query(char *s,int n){
int rt=0,ans=0,pre;
dp[0]=1;pre=1;//pre记当前距离为2的0次方 即长度为0
for(int i=1;i<=n;i++){
rt=nex[rt][s[i-1]-'a'];
dp[i]=0;
if(st[rt]&(pre<<1)){
dp[i]=1,ans=i;
}
pre=(pre<<1)|dp[i];
}
return ans;
}
};
using namespace AC;
int n,m;
char p[M];
int main(){
scanf("%d%d",&n,&m);
init();
for(int i=1;i<=n;i++){
scanf("%s",p);
ins(p,strlen(p));
}
build();
for(int j=1;j<=m;++j){
scanf("%s",p);
printf("%d\n",query(p,strlen(p)));
}
return 0;
}
代码2(朴素回跳)
#include<bits/stdc++.h>
#define N 205
#define M 2000005
using namespace std;
bool dp[M];
namespace AC{
int nex[N][26],num[N],fail[N],q[N],h,t,c,id[N];
void init(){
c=0;
memset(nex[c],0,sizeof nex[c]);
fail[c]=num[c]=0;
id[c]=0;
}
void ins(char *s,int n){
int rt=0;
for(int i=0;i<n;i++){
int v=s[i]-'a';
if(!nex[rt][v]){
nex[rt][v]=++c;
memset(nex[c],0,sizeof nex[c]);
fail[c]=num[c]=0;
id[c]=0;
}
rt=nex[rt][v];
}
id[rt]=n;
}
void build(){
h=1;t=0;
for(int i=0;i<26;i++){
if(nex[0][i]){
fail[nex[0][i]]=0,q[++t]=nex[0][i];
}
}
while(h<=t){
int u=q[h++];
for(int i=0;i<26;i++){
if(nex[u][i])fail[nex[u][i]]=nex[fail[u]][i],q[++t]=nex[u][i];
else nex[u][i]=nex[fail[u]][i];
}
}
}
int query(char *s,int n){
int rt=0,ans=0;
dp[0]=1;
for(int i=1;i<=n;i++){
rt=nex[rt][s[i-1]-'a'];
dp[i]=0;
for(int j=rt;j;j=fail[j]){
if(id[j])dp[i]|=dp[i-id[j]];
if(dp[i])break;
}
if(dp[i])ans=i;
}
return ans;
}
};
using namespace AC;
int n,m;
char p[M];
int main(){
scanf("%d%d",&n,&m);
init();
for(int i=1;i<=n;i++){
scanf("%s",p);
ins(p,strlen(p));
}
build();
for(int j=1;j<=m;++j){
scanf("%s",p);
printf("%d\n",query(p,strlen(p)));
}
return 0;
}