P4584 [FJOI2015] 带子串包含约束LCS问题
题意
给两个串串 \(X,Y\) 和一个字符串的集合 \(S={s_1,s_2,\dots,s_k}\),求 \(X,Y\) 的最长公共子序列满足 \(S\) 中所有串串都是该串的某个字串。
\(|X|,|Y|,s_i\le300,k\le5\)。
思路
子序列自动机:对于每个位置,祂边 \(c\) 的出边连向祂后面第一个字符 \(c\)。
先把约束集的 \(ACAM\) 建出来,然后把 \(X\) 和 \(Y\) 的子序列自动机建出来,设 \(f_{i,j,k,s}\) 表示走到 \(X\) 子序列自动机的走到 \(i\) 号点,\(Y\) 子序列自动机的走到 \(j\) 号点,公共子序列在 \(ACAM\) 上走到位置 \(k\),约束集被覆盖的状态为 \(s\) 的最长公共子序列长度。转移的时候枚举一个字符,三个自动机上同时跳这条边即可。可以使用类似 \(SPFA\) 的方法转移。
理论会 \(T\) 飞,但是实际上跑的飞快。
代码
/*
Luogu P4584 [FJOI2015] 带子串包含约束LCS问题
2026-04-15
*/
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
using namespace std;
namespace IO{
template<typename T>
inline void read(T&x){
x=0;char c=getchar();bool f=0;
while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
f?x=-x:0;
}
template<typename T>
inline void write(T x){
if(x==0){putchar('0');return ;}
x<0?x=-x,putchar('-'):0;short st[50],top=0;
while(x) st[++top]=x%10,x/=10;
while(top) putchar(st[top--]+'0');
}
inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
inline void write(char c){putchar(c);}
inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
const int maxk=10,maxn=310;
int n,m,k,l[maxk];
string X,Y;
int change(char x){
if(x>='a') return x-'a'+26;
return x-'A';
}
class AM{
private:
int to[maxn][60];
public:
void build(string s){
int n=s.size();
for(int i=0;i<52;i++) to[n][i]=-1;
for(int i=n-1;i>=0;i--){
for(int j=0;j<52;j++) to[i][j]=to[i+1][j];
to[i][change(s[i])]=i+1;
}
}
int nxt(int w,int x){return to[w][x];}
}amx,amy;
class ACAM{
private:
struct node{int ch[60],end,fail;}t[maxn*maxk];
int cnt;
public:
void insert(int id,string s){
int u=0;
for(int i:s){
if(!t[u].ch[change(i)]) t[u].ch[change(i)]=++cnt;
u=t[u].ch[change(i)];
}
t[u].end|=(1<<id-1);
}
void build(){
queue<int>q;
for(int i=0;i<52;i++) if(t[0].ch[i]) q.push(t[0].ch[i]);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<52;i++){
if(t[u].ch[i]) q.push(t[u].ch[i]),t[t[u].ch[i]].fail=t[t[u].fail].ch[i],t[t[u].ch[i]].end|=t[t[t[u].ch[i]].fail].end;
else t[u].ch[i]=t[t[u].fail].ch[i];
}
}
}
pair<int,int>nxt(int u,int x){return{t[u].ch[x],t[t[u].ch[x]].end};}
}t;
struct zt{
int w1,w2,w3,s;
bool operator==(const zt&other)const{
return w1==other.w1&&w2==other.w2&&w3==other.w3&&s==other.s;
}
};
struct ZTHash {
static const uint64_t M1=998244353;
static const uint64_t BASE1=131;
size_t operator()(const zt& z)const{
uint64_t h1=((z.w1*BASE1+z.w2)%M1*BASE1+z.w3)%M1;
h1=(h1*BASE1+z.s)%M1;
return h1;
}
};
queue<zt>q;
__gnu_pbds::cc_hash_table<zt,int,ZTHash>f;
__gnu_pbds::cc_hash_table<zt,bool,ZTHash>flag;
signed main(){
read(n,m,k);
for(int i=1;i<=k;i++) read(l[i]);
read(X,Y);
amx.build(X),amy.build(Y);
for(int i=1;i<=k;i++){string s;read(s);t.insert(i,s);}
t.build();
q.push({0,0,0,0});
int ans=0;
while(!q.empty()){
auto[i,j,k,s]=q.front();q.pop();
flag[{i,j,k,s}]=0;
if(s==(1<<::k)-1) ans=max(ans,f[{i,j,k,s}]);
for(int c=0;c<52;c++){
int ii=amx.nxt(i,c),jj=amy.nxt(j,c);
auto[kk,ss]=t.nxt(k,c);
ss|=s;
if(ii==-1||jj==-1) continue;
if(f[{i,j,k,s}]+1>f[{ii,jj,kk,ss}]){
f[{ii,jj,kk,ss}]=f[{i,j,k,s}]+1;
if(!flag[{ii,jj,kk,ss}]) flag[{ii,jj,kk,ss}]=1,q.push({ii,jj,kk,ss});
}
}
}
write(ans);
return 0;
}

浙公网安备 33010602011771号