BZOJ3530:[SDOI2014]数数(AC自动机,数位DP)

Description

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

Input

输入的第一行包含整数N。
接下来一行一个整数M,表示S中元素的数量。
接下来M行,每行一个数字串,表示S中的一个元素。

Output

输出一行一个整数,表示答案模109+7的值。

Sample Input

20
3
2
3
14

Sample Output

14

HINT

下表中l表示N的长度,L表示S中所有串长度之和。

1 < =l < =1200 , 1 < =M < =100 ,1 < =L < =1500

Solution

一个挺简单的一个题……建出来$AC$自动机然后在上面直接跑数位$DP$就好了。只不过有点小地方需要注意。

用$DFS(zero,lim,pos,now)$是否有前导0,是否卡上界,第$pos$位,自动机上第$now$个点。

为什么要存前导0呢?我们可以发现有这么一个例子:

10

1

01

这个跑出来应该是10,然而不记前导零特判一下会跑出来9。这是因为当幸运串为01的时候我们会忽略前导0,所以是合法的。

只需要在$DFS$的时候特判一下,如果有前导0,且幸运数这一位选0,且$now$还在根节点,就让$now$停在根节点就好了。

建立AC自动机的时候,如果某个节点能够沿着fail指针跳到单词节点,那么这个节点也应当禁止通过……

自测一时爽

Code

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<queue>
 5 #define N (1509)
 6 #define MOD (1000000007)
 7 using namespace std;
 8 
 9 int sz,Son[N][11],Fail[N],End[N];
10 int cnt,m,x,f[N][N],a[N],spc[N];
11 char n[N],s[N];
12 queue<int>q;
13 
14 void Insert(char s[])
15 {
16     int now=0;
17     for (int i=0,l=strlen(s); i<l; ++i)
18     {
19         int c=s[i]-'0';
20         if (!Son[now][c]) Son[now][c]=++sz;
21         now=Son[now][c];
22     }
23     End[now]++;
24 }
25 
26 void Build_Fail()
27 {
28     for (int i=0; i<=9; ++i)
29         if (Son[0][i]) q.push(Son[0][i]);
30     while (!q.empty())
31     {
32         int now=q.front(); q.pop();
33         for (int i=0; i<=9; ++i)
34         {
35             if (!Son[now][i])
36             {
37                 Son[now][i]=Son[Fail[now]][i];
38                 continue;
39             }
40             Fail[Son[now][i]]=Son[Fail[now]][i];
41             q.push(Son[now][i]);
42         }
43     }
44 }
45 
46 int DFS(int zero,int lim,int pos,int now)
47 {
48     if (pos==0) return 1;
49     if (!zero && !lim && f[pos][now]!=-1) return f[pos][now];
50     f[pos][now]=0;
51     int up=lim?n[pos]-'0':9;
52     for (int i=0; i<=up; ++i)
53         if (!End[Son[now][i]])
54         {
55             if (zero && !i && !now) (f[pos][now]+=DFS(zero,lim&&0==up,pos-1,0))%=MOD;
56             else
57             {
58                 int flag=1,t=Son[now][i];
59                 while (t)
60                 {
61                     if (End[t]) {flag=0; break;}
62                     t=Fail[t];
63                 }
64                 if (!flag) continue;
65                 (f[pos][now]+=DFS(zero&&!i,lim&&i==up,pos-1,Son[now][i]))%=MOD;
66             }
67         }
68     return f[pos][now];
69 }
70 
71 int main()
72 {
73     memset(f,-1,sizeof(f));
74     scanf("%s%d",n+1,&m); cnt=strlen(n+1);
75     for (int i=1; i<=m; ++i)
76         scanf("%s",s),Insert(s);
77     Build_Fail(); 
78     for (int i=1,j=cnt; i<j; ++i,--j)
79         swap(n[i],n[j]);
80     printf("%d\n",DFS(1,1,cnt,0)-1);
81 }
posted @ 2018-11-29 14:10  Refun  阅读(172)  评论(0编辑  收藏  举报