• 【JSOI2007】文本生成器 题解(AC自动机+动态规划)


    题目链接

    题目大意:给定$n$个子串,要求构造一个长度为$m$的母串使得至少有一个子串是其子串。问方案数。

    ------------------------

    我们可以对要求进行转化:求出不合法的方案数,总方案数减去不合法的方案数即为合法方案数。

    首先建一个AC自动机,对于每个串的末尾结点及其$fail$边指向的结点都打上标记,表示遍历AC自动机的时候不经过这些点(因为如果一个串是另一个串的后缀,显然这两个串都是合法的)。

    然后就可以大力DP了。设$f[i][j]$表示走了$i$步到达$j$结点的方案数。则有转移方程$f[i+1][son(j)]+=f[i][j]$。答案即为$m^{26}-sumlimits_{i=1}^{cnt} f[m][i]$。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int mod=1e4+7;
    int n,m,f[103][6003],cnt,sum,ans=1;
    string ss;
    struct node
    {
        int ch[30],end,fail;
    }tree[60005];
    inline void build(string s)
    {
        int l=s.length(),now=0;
        for (int i=0;i<l;i++)
        {
            if (!tree[now].ch[s[i]-'A']) tree[now].ch[s[i]-'A']=++cnt;
            now=tree[now].ch[s[i]-'A'];  
        }
        tree[now].end=1;
    }
    inline void getfail()
    {
        queue<int> q;
        for (int i=0;i<26;i++)
        {
            if (tree[0].ch[i]) tree[0].fail=0,q.push(tree[0].ch[i]);
        }
        while(!q.empty())
        {
            int now=q.front();q.pop();
            for (int i=0;i<26;i++)
            {
                if (!tree[now].ch[i])
                {
                    tree[now].ch[i]=tree[tree[now].fail].ch[i];
                    continue;
                }
                tree[tree[now].ch[i]].fail=tree[tree[now].fail].ch[i];
                q.push(tree[now].ch[i]);
                tree[tree[now].ch[i]].end|=tree[tree[tree[now].fail].ch[i]].end;
            }
        }
    }
    int main()
    {
        cin>>n>>m;
        for (int i=1;i<=n;i++) cin>>ss,build(ss);
        tree[0].fail=0;
        getfail();
        f[0][0]=1;
        for (int i=1;i<=m;i++)
            for (int j=0;j<=cnt;j++)
                for (int k=0;k<26;k++)
                    if (!tree[tree[j].ch[k]].end)
                        f[i][tree[j].ch[k]]+=f[i-1][j],f[i][tree[j].ch[k]]%=mod;
        for (int i=1;i<=m;i++) ans=(ans*26)%mod;
        for (int i=0;i<=cnt;i++) sum+=f[m][i];
        printf("%d",((ans-sum)%mod+mod)%mod);
        return 0; 
    }
  • 相关阅读:
    Android tcpdump 抓包
    Android CursorAdapter 查询联系人过滤
    Android 项目打包成apk文件
    解决Centos 6.3 中 gedit中文乱码问题
    在Linux(centos)系统上用手机调试android程序(eclipse)
    系统定时关机命令–shutdown
    使用gdb Server调试嵌入式程序
    Vim 错误排查方法
    通过netstat命令查看进程与端口的对应关系
    dexpler的使用方法
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13405822.html
Copyright © 2020-2023  润新知