zoukankan      html  css  js  c++  java
  • BZOJ 1030文本生成器

    第一次做ac自动机+dp的题。因为前日做过一道字符串dp题,这题做起来相对没那么困难一些。觉得一时间这题无法下手可以先试试这场div3的F题:https://blog.csdn.net/weixin_43262291/article/details/98390702
    题意:给你n个模式串,现在构造出m长度仅有26个字母的文本串,使其中至少包含1个模式串,问有多少个。
    思路:很容易想到dp,dpij,i表示构造到第几位,j来枚举ac自动机的状态。在每个状态枚举26个字母转移到新状态。不过在尝试直接构造的情况下会发现,当构造出来一个包含模式串的状态时,转移到下一个状态,并不知道在个状态哪一部分是合法来的哪一部分是不合法的。所以可以利用补集,把到达合法状态的筛掉,那么达到最终状态的dpmj累加起来取26^m的补集则是最终答案。
    细节部分要注意:

    1. 当前面某一个状态的后缀或者说失配对应位置如果包含模式串,那么剩下的也都得包含,所以预处筛掉,这里我在build函数里加了个if(cnt[fail[u]])cnt[u] = 1;如此一层一层往上会层层标记恰好达到效果。
    2. 每次筛掉的是下一个状态是不是,也可算当前状态是不是。
      代码:
    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define forn(i,n) for(int i=0;i<n;i++)
    #define for1(i,n) for(int i=1;i<=n;i++)
    #define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
    const int maxn = 6e3+5;
    const int mod = 1e4+7;
    int dp[105][maxn],fail[maxn],trie[maxn][26],id,cnt[maxn];
    
    class Aho{
        public:
        void insert(string s){
            int len = s.size(),u = 0;
            forn(i,len){
                int x = s[i]-'A';
                if(!trie[u][x]) trie[u][x] = ++id;
                u = trie[u][x];
            }
            cnt[u]++;
        }
        queue<int>q;
        void build(){
            forn(i,26) if(trie[0][i]) q.push(trie[0][i]);
            while(!q.empty()){
                int u = q.front();q.pop();
                if(cnt[fail[u]]) cnt[u] = 1;
                forn(i,26){
                    if(trie[u][i]) fail[trie[u][i]] = trie[fail[u]][i],q.push(trie[u][i]);
                    else trie[u][i] = trie[fail[u]][i];
                }
            }
        }
    }aho;
    
    ll powmod(ll a,ll b){
        ll res = 1;
        while(b){
            if(b&1) res = res*a%mod;
            a = a*a%mod;
            b>>=1;
        }
        return res;
    }
    
    int main(){
        IO;
        int n,m;cin>>n>>m;
        forn(i,n){
            string s;cin>>s;
            aho.insert(s);
        }
        aho.build();
        dp[0][0] = 1;
        forn(i,m){
            forn(j,id)if(!cnt[j]){
                forn(k,26){
                    (dp[i+1][trie[j][k]]+=dp[i][j])%=mod;              
                }
            }
        }
        ll ans = powmod(26,m);
        forn(i,id+1) if(!cnt[i]) (ans=ans+mod-dp[m][i])%=mod;
        cout << ans <<'
    ';
        return 0;
    }
    
    人一我百,人十我万。
  • 相关阅读:
    Oracle数据库对表字段的操作命令
    解决eclipse中git中cannot open gituploadpack(无法打开Git上传包)问题
    C# 启动外部程序的几种方法
    php面试题及答案(转)
    每日知识(1)半结构化数据
    每日知识(2)云计算
    方法对象
    什么是MA移动平均线它的特点及应用
    My first testcase about C#&C++
    局部变量的角色
  • 原文地址:https://www.cnblogs.com/AlexPanda/p/12520319.html
Copyright © 2011-2022 走看看