BZOJ1030 JSOI2007 文本生成器 题解&代码

26 篇文章 0 订阅
11 篇文章 0 订阅

题意:给出n个匹配串,询问:对于长度为m的串,有多少个串至少包含一个匹配串(答案对10007取模)
题解:
“至少包含一个匹配串的长度为m的串”,那么很容易转化为“所有串除去不包含任何匹配串的长度为m的串”
然后就是喜闻乐见的AC自动机上的dp了,dp方程显然是dp[i][j]表示长度为i的串匹配到j位时有多少不包含任何匹配串
有:dp[i][ch[j][k]]+=dp[i-1][j]
即孩子节点一定由有效父亲节点推知(节点本身flag指针就是true的当然是0)

/**************************************************************
    Problem: 1030
    User: Rainbow6174
    Language: C++
    Result: Accepted
    Time:76 ms
    Memory:4396 kb
****************************************************************/

#include <iostream>
#include <cstdio>
#include <cstring>
#define MOD 10007
using namespace std;
const int maxt = 6005;
int n, m, tot, ans = 1, temp, ch[maxt][26], fail[maxt], q[maxt], dp[105][maxt];
char s[105];
bool flag[maxt];
void newnode(int x, int v)
{
    ch[x][v]=++tot;
}
void addtrie(char s[])
{
    int x = 0, p = 0, len = strlen(s);
    while( p < len )
    {
        temp = s[p]-'A';
        if ( !ch[x][temp] ) newnode(x, temp);
        x = ch[x][temp];
        p++;
    }
    flag[x] = 1;
}
void AC(void)
{
    int h = 0, t = 0;
    for(int i = 0; i < 26; i++)
        if(ch[0][i]) q[t++] = ch[0][i];
    while( h < t )
    {
        temp = q[h++];
        for(int i = 0; i < 26; i++)
            if( !ch[temp][i] ) ch[temp][i] = ch[fail[temp]][i];
            else fail[ch[temp][i]] = ch[fail[temp]][i],
                flag[ch[temp][i]] |= flag[fail[ch[temp][i]]],
                q[t++] = ch[temp][i];
    }
}
int main(void)
{
    scanf("%d%d", &n, &m);
    for(int i = 0; i < n; i++)
    {
        scanf("%s", s);
        addtrie(s);
    }
    AC();
    dp[0][0] = 1;
    for(int i = 1; i <= m; i++)
    {
        for(int j = 0; j <= tot; j++)
        {
            if(flag[j] || !dp[i-1][j]) continue;
            for(int k = 0; k < 26; k++)
            {
                dp[i][ch[j][k]] += dp[i-1][j];
                dp[i][ch[j][k]] %= MOD;
            }
        }
        ans*=26;
        ans%=MOD;
    }
    //cout<<ans<<endl;
    for(int i = 0; i <= tot; i++)
    {
        //cout<<i<<' '<<flag[i]<<endl;
        if(!flag[i])
        {
            //cout<<dp[m][i]<<endl;
            ans -= dp[m][i];
            ans += MOD;
            ans %= MOD;
        }
    }
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值