bzoj 1030: [JSOI2007]文本生成器

本文介绍了一种将AC自动机与动态规划(DP)相结合的算法,用于解决字符串匹配问题。通过构建AC自动机并利用DP计算非法子串数量,从而快速得出合法字符串的总数。文章详细解释了算法原理及其实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

AC自动机上跑DP 

注意的细节是fail链上有标记 该节点也要标记(就好比 “ ********123******** ”  索然最后没有出现123 但是中间出现了123  这种情况不可取!) 

 

/**************************************************************
    Problem: 1030
    User: lxy8584099
    Language: C++
    Result: Accepted
    Time:132 ms
    Memory:6044 kb
****************************************************************/
 
/*
    有点像 1009 TG考试 不过这个数据挺小 不用矩阵加速 
    我们求出不合法的个数 用总数减去就行了
    一个串的时候我们用KMP转移 
    因为有多个串 所以建个AC自动机 转移 
    之后的就和1009一模一样了
    f[i][j]表示 表示前i个字符 目前节点为j 的不符合方案数
    转移: f[i][ c[j][k] ] += f[i-1][j] (c[j][k]不是某个认识单词的结尾) 
    自动机没有儿子自动接上fail 方便!!! 
    考虑到如果某节点fail链跳到 0 的路上也遇到标记的话 
        这个点也是不可取的! 
        处理方法:我们在自动机上用或运算。 
*/
#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e4+50;
const int MOD=10007;
int c[N][26],last[N],n,m,tot;
int f[105][N],ans=0,sum=1;
bool vis[N];
void Add(char *s)
{
    int l=strlen(s),u=0;
    for(int i=0;i<l;i++)
    {
        int v=s[i]-'A';
        if(!c[u][v]) c[u][v]=++tot;
        u=c[u][v];
    } vis[u]=1;
}
void Init()
{
    scanf("%d%d",&n,&m);char s[105];
    for(int i=1;i<=n;i++) scanf("%s",s),Add(s);
}
void Ac_build()
{
    queue<int> q;
    for(int i=0;i<26;i++)
        if(c[0][i]) q.push(c[0][i]);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int v=0;v<26;v++)
        {
            if(c[u][v])
            {
                last[c[u][v]]=c[last[u]][v];
                vis[c[u][v]]|=vis[c[last[u]][v]];
                // fial链有标记 该节点不能到达! 
                q.push(c[u][v]);continue;
            }
            c[u][v]=c[last[u]][v]; // 这叫啥。。忘了。 
        }
             
    }
}
void Solve() 
{
    f[0][0]=1;
    for(int i=0;i<m;i++)
        for(int u=0;u<=tot;u++)
            for(int v=0;v<26;v++) if(!vis[c[u][v]])
                (f[i+1][c[u][v]]+=f[i][u])%=MOD;
    for(int i=0;i<=tot;i++) (ans+=f[m][i])%=MOD;
    for(int i=1;i<=m;i++) (sum*=26)%=MOD;
    printf("%d\n",(sum-ans+MOD)%MOD);
}
int main()
{
    Init();
    Ac_build();
    Solve();
    return 0;
}

 

转载于:https://www.cnblogs.com/lxy8584099/p/10191955.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值