P3311 数数

P3311 数数

题意:

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

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

思路:

数位dp + AC自动机

实现:

#include <stdio.h>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
const int N = 1505, mod = 1e9 + 7;
int idx = 1, tr[N][12], cnt[N], q[N], nex[N];
//首位是k,到第i位,匹配到j
long long f[N][N][2][2];
char s[N];
char num[N];
int m;
void insert()
{
    int p = 0;
    for (int i = 1; s[i]; i++)
    {
        int t = s[i] - '0';
        if (!tr[p][t])
            tr[p][t] = idx++;
        p = tr[p][t];
    }
    cnt[p] = 1;
}
void build()
{
    int hh = 1, tt = 0;
    for (int i = 0; i <= 9; i++)
        if (tr[0][i])
            q[++tt] = tr[0][i];
    while (hh <= tt)
    {
        int t = q[hh++];
        for (int i = 0; i <= 9; i++)
        {
            int &p = tr[t][i];
            if (!p)
                p = tr[nex[t]][i];
            else
            {
                nex[p] = tr[nex[t]][i];
                q[++tt] = p;
                cnt[p] |= cnt[nex[p]];
            }
        }
    }
}
// pos:当前处理第几位,trie_pos当前的状态,limit当前是否被限制,lead是否有前导0
int dfs(int pos, int trie_pos, bool limit, bool lead)
{
    if (!pos)
        return !cnt[trie_pos];
    if (cnt[trie_pos])
        return 0;
    if (!limit && !lead && f[pos][trie_pos][limit][lead] != -1)
        return f[pos][trie_pos][limit][lead];
    int up = limit ? (num[pos] - '0') : 9, res = 0;
    for (int i = 0; i <= up; i++)
        //               处理下一位       如果填0并且有前导0     前面顶了上限并且当前也顶上限下一位就会被限制       前面有前导0,或当前位0
        res = ((int)res + dfs(pos - 1, (lead & !i) ? 0 : tr[trie_pos][i], limit & (i == up), lead & !i)) % mod;
    //记忆化
    f[pos][trie_pos][limit][lead] = res;
    return res;
}

int main()
{
    scanf("%s%d", num + 1, &m);
    for (int i = 1; i <= m; i++)
    {
        scanf("%s", s + 1);
        insert();
    }
    build();
    int len = strlen(num + 1);
    reverse(num + 1, num + len + 1);
    memset(f, -1, sizeof f);
    printf("%d\n", dfs(len, 0, 1, 1) - 1);
    return 0;
}

posted @ 2022-08-07 18:03  zxr000  阅读(70)  评论(0编辑  收藏  举报