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;
}
posted @ 2026-04-16 22:34  Link-Cut_Trees  阅读(6)  评论(0)    收藏  举报