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
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 }