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 <<'\n';
    return 0;
}
posted @ 2019-08-06 21:27  AlexPanda  阅读(127)  评论(0编辑  收藏  举报