BZOJ 1030 [JSOI2007]文本生成器

本文介绍了一种结合AC自动机与动态规划(DP)的算法实现,用于解决特定字符串匹配问题。通过构建AC自动机并运用DP计算合法字符串方案数,最终求解在给定长度下不包含预设模式的字符串总数。

AC自动机+DP。

这题和POJ2778神似。不同的是本题要求包含字串,而那POJ那一题要求不包含。

发现只要m一定,总数量是一定的,为 26m 。于是正难则反- -,用总数量减去不包含任何串的数量。

这题节点太多(最坏情况上千个),不能用矩阵做,于是DP。记f[i][j]表示以i节点为起点,长度为j的合法字符串方案数,往儿子DP即可。

#include<cstdio>
#include<queue>
#include<cstring>
#define L 105
#define N 60
#define MOD 10007
using namespace std;
int n, m, cnt;
long long f[N*L][L];
char s[L];
struct node
{
    node *next[27], *fail;
    bool flag;
    int id;
    node()
    {
        memset(next,NULL,sizeof(next));
        fail=NULL;
        flag=0;
        id=++cnt;
    }
};
struct ACAutoMaton
{
    node *root;
    void init()
    {
        root=new node;
        root->fail=root;
    }
    void inser()
    {
        node *p=root;
        for(int i = 0; s[i]; i++)
        {
            int c=s[i]-'A';
            if(p->next[c]==NULL)
                p->next[c]=new node;
            p=p->next[c];
        }
        p->flag=1;
    }
    void build()
    {
        queue<node*> q;
        for(int i = 0; i < 26; i++)
        {
            if(root->next[i])
            {
                root->next[i]->fail=root;
                q.push(root->next[i]);
            }
            else
                root->next[i]=root;
        }
        while(!q.empty())
        {
            node *u = q.front();
            q.pop();
            for(int i = 0; i < 26; i++)
            {
                if(u->next[i]==NULL)
                {
                    u->next[i]=u->fail->next[i];
                    continue;
                }
                node *v = u->next[i], *t = u->fail;
                q.push(v);
                while(t!=root && t->next[i]==NULL)
                    t=t->fail;
                v->fail=t->next[i]?t->next[i]:root;
                v->flag|=v->fail->flag;
            }
        }
    }
    void dp_pre()
    {
        queue<node*> q;
        q.push(root);
        for(int i = 0; i < 26; i++)
            if(!root->next[i]->flag)
                f[root->id][1]++;
        while(!q.empty())
        {
            node *u=q.front();
            q.pop();
            for(int i = 0; i < 26; i++)
            {
                node *v=u->next[i];
                if(f[v->id][1])continue;
                for(int i = 0; i < 26; i++)
                    if(!v->next[i]->flag)
                        f[v->id][1]++;
                q.push(v);
            }
        }
    }
    void dp(node *p, int step)
    {
        int id=p->id;
        if(f[id][step])return;
        for(int i = 0; i < 26; i++)
        {
            if(p->next[i]->flag)continue;
            dp(p->next[i],step-1);
            f[id][step]=(f[id][step]+f[p->next[i]->id][step-1])%MOD;
        }
    }
}AC;
int main()
{
    AC.init();
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; i++)
    {
        scanf("%s",s);
        AC.inser();
    }
    AC.build();
    AC.dp_pre();
    AC.dp(AC.root,m);
    int tot=1, ans=f[AC.root->id][m];
    for(int i = 1; i <= m; i++)tot=(tot*26)%MOD;
    printf("%d\n",((tot-ans)%MOD+MOD)%MOD);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值