嘘~ 正在从服务器偷取页面 . . .

洛谷P3311 [SDOI2014] 数数 题解


洛谷P3311 [SDOI2014] 数数 题解

题目链接:P3311 [SDOI2014] 数数

题意

我们称一个正整数 \(x\) 是幸运数,当且仅当它的十进制表示中不包含数字串集合 \(s\) 中任意一个元素作为其子串。例如当 \(s = \{22, 333, 0233\}\) 时,\(233\) 是幸运数,\(2333\)\(20233\)\(3223\) 不是幸运数。给定 \(n\)\(s\),计算不大于 \(n\) 的幸运数个数。

答案对 \(10^9 + 7\) 取模。

\(1 \leq n < 10^{1201}\)\(1 \leq m \leq 100\)\(1 \leq \sum_{i = 1}^m |s_i| \leq 1500\)\(\min_{i = 1}^m |s_i| \geq 1\),其中 \(|s_i|\) 表示字符串 \(s_i\) 的长度。\(n\) 没有前导 \(0\),但是 \(s_i\) 可能有前导 \(0\)

关键词:数位dp、AC自动机

可能的前置芝士:P4052 [JSOI2007]文本生成器 (涉及“危险结点”的定义)

和P4052一样,我们先建出 \(\tt{AC}\) 自动机,然后找到所有的危险结点

因为这道题要求答案不超过 \(n\) ,并且 \(n < 10^{1201}\) ,考虑数位dp

\(f_{i,j}\) 表示考虑 \(n\) 的前 \(i\) 位(从高位向低位数),走到 \(\tt{AC}\) 自动机的 \(j\) 结点的答案。

然后用记搜搞搞就好了,懒得写转移方程,直接看代码

值得注意的是,如果数位dp的过程中 \(j\) 走到了危险结点,

则要立刻返回 \(0\) ,因为不能包含任何的非法子串 \(s_i\)

时间复杂度 \(O(n \times \sum |s_i|)\)

代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdarg>
#include <cmath>
#include <iomanip>
#include <random>
#include <queue>
#include <bitset>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(1515)

const int mod=1e9+7;
int n,num[N],f[N][N];
struct Trie
{
    bitset<N> ed;
    int tot,fail[N],trie[N][10];
    void insert(string s)
    {
        int u=0;
        for(int i=0; i<s.size(); i++)
        {
            char c=s[i]-'0';
            if(!trie[u][c]) trie[u][c]=++tot;
            u=trie[u][c];
        }
        ed[u]=1;
    }
    void build()
    {
        queue<int> q;
        for(int i=0; i<10; i++)
            if(trie[0][i]) q.push(trie[0][i]);
        while(!q.empty())
        {
            int u=q.front(); q.pop();
            for(int i=0; i<10; i++)
            {
                if(trie[u][i])
                {
                    fail[trie[u][i]]=trie[fail[u]][i];
                    if(ed[fail[trie[u][i]]]) ed[trie[u][i]]=1;
                    q.push(trie[u][i]);
                }else trie[u][i]=trie[fail[u]][i];
            }
        }
    }
}tr;
void add(int &x,int y){ (x+=y) >= mod ? x-=mod : 0;}
int dfs(int u,int pos,bool limit,bool qd0)
{
    if(!u) return !tr.ed[pos];
    if(tr.ed[pos]) return 0;
    if(!limit&&!qd0&&f[u][pos]!=-1) return f[u][pos];
    int res=0,up=limit?num[u]:9;
    for(int i=0; i<=up; i++)
    {
        if(!i&&qd0) add(res,dfs(u-1,0,limit&&num[u]==i,1));
        else add(res,dfs(u-1,tr.trie[pos][i],limit&&num[u]==i,0));
    }
    if(!limit&&!qd0) f[u][pos]=res;
    return res;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    string s; cin >> s >> n;
    int len=s.size();
    for(int i=0; i<len; i++) num[len-i]=s[i]-'0';
    for(int i=1; i<=n; i++) cin >> s,tr.insert(s);
    
    tr.build(); memset(f,-1,sizeof(f));
    int ans=dfs(len,0,1,1); add(ans,mod-1); // no 0
    cout << ans << '\n';
    return 0;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录