隐藏
「SDOI2014」数数 - AC自动机+数位动规 | Bill Yang's Blog

路终会有尽头,但视野总能看到更远的地方。

0%

「SDOI2014」数数 - AC自动机+数位动规

题目大意

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


题目分析

限制一些数字串不能选择,统计可选数字串总数。
这样一个问题不难想到AC自动机。
设$f[i,j]$为长度为$i$的串,当前在AC自动机$j$号结点的合法数字串个数。

但因为限制了数字选择的上界,故考虑使用数位动规的形式对上述方程进行转移。

维护两个标记$up,zero$表示是否顶上界与是否有前导零。
在$up==0$时记录对动规结果记忆化。

注意坑:当有数位动规存在前导零时,保持AC自动机指针指向根结点。
至于为什么请读者自行思考。


代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<climits>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;

typedef long long LL;

inline const int Get_Int() {
int num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9') {
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9') {
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}

const int maxn=2005;
const LL mod=1e9+7;

struct Aho_Corasick_Automaton {
struct Tree {
int child[10],fail,next,cnt;
bool flag;
} tree[maxn];
int root,cnt;
#define ch(x,i) tree[x].child[i]
#define fail(x) tree[x].fail

Aho_Corasick_Automaton() {
init();
}

void init() {
root=cnt=1;
memset(tree,0,sizeof(tree));
}

int insert(string s) {
int now=root;
for(auto x:s) {
int j=x-'0';
if(!ch(now,j))ch(now,j)=++cnt;
now=ch(now,j);
}
tree[now].cnt++;
tree[now].flag=1;
return now;
}

void buildfail() {
queue<int>Q;
Q.push(root);
while(!Q.empty()) {
int now=Q.front();
Q.pop();
for(int i=0; i<10; i++) {
int& next=ch(now,i);
if(!next) {
next=ch(fail(now),i)?ch(fail(now),i):root;
continue;
}
Q.push(next);
int p=fail(now);
if(p)fail(next)=ch(p,i);
else fail(next)=root;
tree[next].next=tree[fail(next)].cnt?fail(next):tree[fail(next)].next;
tree[next].flag|=tree[tree[next].next].flag;
}
}
}
} acam;

char n[maxn];
int len,m;
LL f[maxn][maxn];

LL Dp(int Now,int pos,bool up,bool zero) {
if(acam.tree[pos].flag)return 0;
if(Now==len)return !zero;
if(!zero&&!up&&~f[Now][pos])return f[Now][pos];
int limit=up?n[Now]-'0':9;
LL sum=0;
for(int i=0; i<=limit; i++) {
bool _zero=zero&&i==0;
sum=(sum+Dp(Now+1,_zero?1:acam.tree[pos].child[i],up&&i==limit,_zero))%mod;
}
if(!zero&&!up)f[Now][pos]=sum;
return sum;
}

int main() {
memset(f,-1,sizeof(f));
scanf("%s",n);
len=strlen(n);
m=Get_Int();
for(int i=1; i<=m; i++) {
char s[maxn];
scanf("%s",s);
int len=strlen(s);
acam.insert(s);
}
acam.buildfail();
printf("%lld\n",Dp(0,1,1,1));
return 0;
}
姥爷们赏瓶冰阔落吧~