Luogu P2292 HNOI2004 L 语言 题解 [ 紫 ] [ AC 自动机 ] [ 状压 dp ]

L 语言:很好的一道状压 dp 题。

思路

看到这题,首先可以想到一个很暴力的 dp,设 \(dp_i\) 表示考虑到第 \(i\) 位能否被理解,暴力匹配字符串转移即可。

第一个优化也很显然,暴力匹配字符串换成 AC 自动机即可。

但是时间复杂度变成了 \(O(m|T||S|)\) 的,显然会被卡。

状压与位运算优化

观察到 \(|S|=20\),所以我们最多只能往前转移 \(20\) 位,这就启发我们用一个 int 存下 dp 状态,用位运算优化转移。这个技巧通常用于可行性 dp 也就是只有 \(0/1\) 状态的 dp 里。

同时我们也需要在 fail 树上预处理出某个节点能接受转移的长度,然后每次和当前 dp 状态与运算一下就好了。

时间复杂度 \(O(m|T|)\)

坑点

dp 状态取模的时候要取 \(2_i\),如果任取一个数那么会导致 dp 完全乱掉。反例也很好举,例如 \(114514 \bmod 2^{10} \ne 114514 \bmod 1000\)

同时字典树 insert 要用 insert(s) 而不是 insert(s+1),我已经错了两遍以上了。

代码

#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
const int mod=2097152;
int n,m,ch[1005][30],idx=0,tot[1005],ne[1005],dp=0,ans=0;
vector<int>g[1005];
char s[1005],t[4000005];
void insert(char *s)
{
    int p=0;

    for(int i=1;s[i];i++)
    {
        int c=s[i]-'a';
        if(ch[p][c]==0)ch[p][c]=++idx;
        p=ch[p][c];
    }
    tot[p]=(tot[p]|(1<<(strlen(s+1))));
}
void build()
{
    queue<int>q;
    for(int i=0;i<26;i++)
    {
        if(ch[0][i])q.push(ch[0][i]);
    }
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=0;i<26;i++)
        {
            int v=ch[u][i];
            if(v)ne[v]=ch[ne[u]][i],q.push(v);
            else ch[u][i]=ch[ne[u]][i];
        }
    }
}
void dfs1(int u)
{
    for(auto v:g[u])
    {
        tot[v]=(tot[v]|tot[u]);
        dfs1(v);
    }
}
void init()
{
    for(int i=1;i<=idx;i++)g[ne[i]].push_back(i);
    dfs1(0);
}
void solve()
{
    cin>>t+1;
    int len=strlen(t+1),p=0;
    dp=1;
    ans=0;
    for(int i=1;i<=len;i++)
    {
        int c=t[i]-'a';
        p=ch[p][c];
        int now=tot[p];
        dp<<=1;
        dp%=mod;
        int hv=(dp&now);
        if(hv)dp|=1,ans=i;
        if(dp==0)break;
    }
    cout<<ans<<'\n';
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>s+1;
        insert(s);
    }
    build();
    init();
    while(m--)solve();
    return 0;
}
posted @ 2025-01-08 21:03  KS_Fszha  阅读(1)  评论(0编辑  收藏  举报