bzoj1030: [JSOI2007]文本生成器(AC自动机+Dp)

31 篇文章 0 订阅
2 篇文章 0 订阅

题目传送门
好题啊。

解法:
直接求很麻烦。
所以转化为总方案减去不合法的方案。
那么不合法的方案就相当于在字典树上面没有经过结尾节点的路径条数。
那么用f[i][j]表示走i步到了第j个节点的方案数。
对应的它下一步走到k。那么f[i+1][k]+=f[i][j]。
但是会出现一个问题。

就是。
比如说两个串。
abcd
cdef

那么当我们匹配了cd的时候其实应该可以继续匹配e的。
但是假设我们到了d,d是在abcd这条链上面的,我们就不能往下了因为下面没有了(在字典树上)
所以我们应该d连向e。。这样走到d时才能继续匹配e。

然后还有一点要注意的是:
因为我们是在结尾打标记。
然后如果他的fail指针指向的点有标记的话他自己也要打上标记因为你不能走到这里啊。

代码实现:

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
using namespace std;
struct node{int s,w[30],fail;node(){s=fail=0;memset(w,0,sizeof(w));}}tr[11000];int trlen;
int a[1100000],len;
void bt() {
    int now=0;
    for(int i=1;i<=len;i++) {
        int x=a[i];
        if(tr[now].w[x]==0)tr[now].w[x]=++trlen;
        now=tr[now].w[x];
    }tr[now].s=1;
}
int list[510000],head,tail;
void bfs() {
    head=1,tail=2;list[1]=0;
    while(head!=tail) {
        int x=list[head];
        for(int i=1;i<=26;i++) if(tr[x].w[i]==0)tr[x].w[i]=tr[tr[x].fail].w[i];  //如果我的fail有这个孩子就连边
            else  {
                if(x!=0)tr[tr[x].w[i]].fail=tr[tr[x].fail].w[i];
                if(tr[tr[tr[x].fail].w[i]].s==1)tr[tr[x].w[i]].s=1;
                list[tail++]=tr[x].w[i];
            }                   
        head++;
    }
}
char s[1100000];
int f[210][11000];
const int mod=10007;
int main() {
    trlen=0;
    int n,m;scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) {
        scanf("%s",s+1);len=strlen(s+1);
        for(int j=1;j<=len;j++)a[j]=s[j]-'A'+1;bt();
    }
    bfs();memset(f,0,sizeof(f));f[0][0]=1;
    for(int i=0;i<m;i++)for(int j=0;j<=trlen;j++) {
        if(tr[j].s==1)continue;
        for(int x=1;x<=26;x++) {
            int k=tr[j].w[x];if(tr[k].s==1)continue;  //不能走到结尾
            f[i+1][k]=(f[i+1][k]+f[i][j])%mod;
        }
    }
    int ans=1;for(int i=1;i<=m;i++)ans*=26,ans%=mod;
    for(int i=0;i<=trlen;i++) if(tr[i].s==0)ans=(ans-f[m][i]+mod)%mod;printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值