[BZOJ1030][JSOI2007]文本生成器

BZOJ
Luogu

sol

至少出现一次?这怎么\(DP\)
然而:

至少出现一次的方案数=总方案数-一次都没有出现的方案数

所以就把给出的字符串当作是不合法字符然后跑\(DP\)就可以了。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N = 6005;
const int mod = 10007;
int n,m,tot,ch[26][N],fail[N],frb[N],dp[105][N];
char s[N];
queue<int>Q;
int fastpow(int a,int b)
{
    int res=1;
    while (b) {if (b&1) res=res*a%mod;a=a*a%mod;b>>=1;}
    return res;
}
void Insert()
{
    scanf("%s",s);int l=strlen(s),x=0;
    for (int i=0;i<l;i++)
    {
        if (!ch[s[i]-'A'][x]) ch[s[i]-'A'][x]=++tot;
        x=ch[s[i]-'A'][x];
    }
    frb[x]=1;
}
void Get_Fail()
{
    for (int i=0;i<26;i++) if (ch[i][0]) Q.push(ch[i][0]);
    while (!Q.empty())
    {
        int u=Q.front();Q.pop();
        for (int i=0;i<26;i++)
            if (ch[i][u]) fail[ch[i][u]]=ch[i][fail[u]],Q.push(ch[i][u]);
            else ch[i][u]=ch[i][fail[u]];
        frb[u]|=frb[fail[u]];
    }
}
int DP()
{
    dp[0][0]=1;
    for (int i=1;i<=m;i++)
        for (int j=0;j<=tot;j++)
            if (!frb[j])
                for (int k=0;k<26;k++)
                    if (!frb[ch[k][j]])
                        (dp[i][ch[k][j]]+=dp[i-1][j])%=mod;
    int res=0;
    for (int i=0;i<=tot;i++) (res+=dp[m][i])%=mod;
    return res;
}
int main()
{
    scanf("%d %d",&n,&m);
    for (int i=1;i<=n;i++) Insert();
    Get_Fail();
    printf("%d\n",(fastpow(26,m)-DP()+mod)%mod);
    return 0;
}
posted @ 2018-01-25 10:53  租酥雨  阅读(185)  评论(0编辑  收藏  举报