BZOJ[1030][JSOI2007]文本生成器 AC自动机+dp

本文介绍了一道关于AC自动机结合动态规划的算法题,通过构建AC自动机并运用DP求解匹配字符串的问题。文章提供了完整的代码实现,包括快速幂运算、AC自动机构建、失败指针设置及DP状态转移等关键步骤。

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

题目链接http://www.lydsy.com/JudgeOnline/problem.php?id=1030

要统计所有能匹配到一些串的,
只需将串的总数( 26m )减去匹配不到的就可以了
建完AC自动机后,将所有危险点标记,在AC自动机上dp
fi,j 表示匹配j个字母后到AC自动机上的点i上,不与危险串匹配的方案有多少
如果i和他的儿子x都不危险,那么 fx,j+1=fi,j+fx,j+1 (j为枚举的步数)
统计答案时把所有 fi,m 加起来,用 26m 减去就可以了

题目没给m,自己测大概要开到100左右

代码如下:

#include<cstring>
#include<ctype.h>
#include<cstdio>
#include<queue>
#define MOD 10007
using namespace std;
int n,m,cnt,ans;
int f[100020][150];
char s[1020];
inline int quick_pow(int x,int k){
    int sum=1;
    while(k){
        if(k&1) sum=sum*x%MOD;
        x=x*x%MOD;
        k=k>>1;
    }
    return sum%MOD;
}
struct Node{
    Node *ch[26],*nex;
    bool b;
    int num;
    Node();
}*root=new Node,*pre[100020];
Node::Node():b(false),nex(NULL){
    for(int i=0;i<26;i++)
        ch[i]=NULL;
    num=++cnt;
    pre[cnt]=this;
}
queue<Node*>q;
inline void Insert(char *s){
    int len=strlen(s+1);
    Node *x=root;
    for(int i=1;i<=len;i++){
        if(!x->ch[s[i]-'A']) x->ch[s[i]-'A']=new Node;
        x=x->ch[s[i]-'A'];
    }
    x->b=true;
}
inline void GetFail(){
    for(int i=0;i<26;i++){
        if(root->ch[i]) q.push(root->ch[i]),root->ch[i]->nex=root;
        else root->ch[i]=root;
    }
    while(!q.empty()){
        Node *x=q.front();q.pop();
        for(int i=0;i<26;i++){
            if(x->ch[i]) x->ch[i]->nex=x->nex->ch[i],q.push(x->ch[i]);
            else x->ch[i]=x->nex->ch[i];
        }
        Node *tmp=x->nex;
        while(tmp!=root && !tmp->b) tmp=tmp->nex;
        if(tmp->b) x->b=true;
    }
}
void dp(){
    f[root->num][0]=1;
    for(int k=0;k<m;k++)
        for(int i=1;i<=cnt;i++){
            if(pre[i]->b || !f[i][k]) continue;
            for(int j=0;j<26;j++)
                if(!pre[i]->ch[j]->b)
                    f[pre[i]->ch[j]->num][k+1]=(f[pre[i]->ch[j]->num][k+1]+f[i][k])%MOD;
        }
    for(int i=0;i<=cnt;i++) ans=(ans+f[i][m])%MOD;
    return;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%s",s+1);
        Insert(s);
    }
    GetFail();
    dp();
    ans=(quick_pow(26,m)+MOD-ans)%MOD;
    printf("%d",ans);
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值