bzoj 3530[Sdoi2014]数数 - AC自动机 + dp

3530: [Sdoi2014]数数

Time Limit: 10 Sec  Memory Limit: 512 MB

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

 

对于这类问题,我们可以用 AC自动机套dp 来解决

因为对于位数比N少的,有前导零不好处理,所以我们分开讨论

如果位数比N少,就和N的大小没有关系,我们可以直接转移

g[i][j] 表示 当前是第 i 位 并且 走到 AC自动机上的 j 节点的方案数

转移方程就是  g[i + 1][nxt[j]] += g[i][j] (!judge[nxt[j]]);

ANS = sum(g[i][j]) (i = 1 ~ N - 1, j = 0 ~ tot);

对于 位数和N相同的类似操作, 只需要加一维 表示前缀是否和N相同  0 表示不同, 1 表示相同

如果是 0 的话后面就随便加, 1 再判断一下 , 和数位dp 思想很像

 

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <queue> 
  5 #include <algorithm>
  6 #define LL long long
  7 
  8 using namespace std; 
  9  
 10 queue<int> q;
 11 
 12 LL ans = 0;
 13 
 14 int N, M;
 15 int ln;
 16 char s[2000]; 
 17 LL g[1300][1600];
 18 LL f[2][1300][1600]; // 1 有限制, 0 表示无限制 
 19 int tot = 0;
 20 LL mod = 1e9 + 7;
 21 
 22 inline LL read()
 23 {
 24     LL x = 0, w = 1; char ch = 0;
 25     while(ch < '0' || ch > '9') {
 26         if(ch == '-') {
 27             w = -1;
 28         }
 29         ch = getchar();
 30     }
 31     while(ch >= '0' && ch <= '9') {
 32         x = x * 10 + ch - '0';
 33         ch = getchar();
 34     }
 35     return x * w;
 36 }
 37 
 38 const int MAXN = 1e6 + 10;
 39 
 40 int d[MAXN];
 41 
 42 struct trie {
 43     int str[15];
 44     int fail;
 45     int num;
 46     int judge;
 47     void init()
 48     {
 49         memset(str, 0, sizeof str);
 50         fail = 0;
 51         judge = 0;
 52     }
 53 } t[MAXN];
 54 
 55 void insert(int root, int k)
 56 {
 57     for(int i = 1; i <= k; i++) {
 58         int nxt = s[i] - '0';
 59         if(t[root].str[nxt] == 0) {
 60             t[root].str[nxt] = ++tot;
 61             t[tot].init();
 62             t[tot].num = nxt; 
 63         }
 64         root = t[root].str[nxt];
 65     }
 66     t[root].judge = 1;
 67 }
 68 
 69 void getfail()
 70 {
 71     for(int i = 0; i <= 9; i++) {
 72         if(t[0].str[i]) {
 73             q.push(t[0].str[i]);
 74         }
 75     }
 76     while(!q.empty()) {
 77         int tt = q.front();
 78         q.pop();
 79         for(int j = 0; j <= 9; j++) {
 80             if(t[tt].str[j]) {
 81                 int f = t[tt].fail;
 82                 while(t[f].str[j] == 0) {
 83                     if(f == 0) {
 84                         break;
 85                     }
 86                     f = t[f].fail;
 87                 }
 88                 t[t[tt].str[j]].fail = t[f].str[j];
 89                 q.push(t[tt].str[j]);
 90             }
 91         }
 92     }
 93 }
 94 
 95 int main()
 96 {
 97     scanf("%s", s + 1);
 98     ln = strlen(s + 1);
 99     for(int i = 1; i <= ln; i++) {
100         d[i] = s[i] - '0';
101     }
102     M = read();
103     for(int i = 1; i <= M; i++) {
104         scanf("%s", s + 1);
105         int len = strlen(s + 1);
106         insert(0, len);
107     }
108     getfail();
109     g[0][0] = 1;
110     for(int i = 0; i < ln; i++) {
111         for(int j = 0; j <= tot; j++) {
112             for(int k = 0; k <= 9; k++) {
113                 if(i == 0 && k == 0) {
114                     continue;
115                 }
116                 int nxt = t[j].str[k];
117                 int cnt = j;
118                 while(nxt == 0) {
119                     if(cnt == 0) {
120                         break;
121                     }
122                     cnt = t[cnt].fail;
123                     nxt = t[cnt].str[k];
124                 }
125                 if(!t[nxt].judge) {
126                     g[i + 1][nxt] = (g[i + 1][nxt] + g[i][j]) % mod;
127                 }
128             }
129         }
130     }
131     f[1][0][0] = 1;
132     for(int i = 0; i < ln; i++) {
133         for(int j = 0; j <= tot; j++) {
134             for(int k = 0; k <= 9; k++) {
135                 if(i == 0 && k == 0) {
136                     continue;
137                 }
138                 int nxt = t[j].str[k];
139                 int cnt = j;
140                 while(nxt == 0) {
141                     if(cnt == 0) {
142                         break;
143                     }
144                     cnt = t[cnt].fail;
145                     nxt = t[cnt].str[k];
146                 }
147                 if(!t[nxt].judge) {
148                     if(k < d[i + 1]) {
149                         f[0][i + 1][nxt] = (f[0][i + 1][nxt] + f[1][i][j] + f[0][i][j]) % mod;
150                     } else if(k == d[i + 1]) {
151                         f[0][i + 1][nxt] = (f[0][i + 1][nxt] + f[0][i][j]) % mod;
152                         f[1][i + 1][nxt] = (f[1][i + 1][nxt] + f[1][i][j]) % mod;
153                     } else {
154                         f[0][i + 1][nxt] = (f[0][i + 1][nxt] + f[0][i][j]) % mod;
155                     }
156                 }
157             }
158         }
159     }
160     for(int i = 1; i < ln; i++) {
161         for(int j = 0; j <= tot; j++) {
162             ans = (ans + g[i][j]) % mod; 
163         }
164     }
165     /*for(int i = 1; i <= ln; i++) {
166         for(int j = 0; j <= tot; j++) {
167             cout<<t[j].num<<" "<<f[0][i][j]<<" "<<f[1][i][j]<<endl;
168         }
169         cout<<endl<<endl;
170     }*/
171     for(int j = 0; j <= tot; j++) {
172         ans = (ans + f[0][ln][j] + f[1][ln][j]) % mod;
173     }
174     printf("%lld\n", ans);
175 }
176 
177 
178 
179 /*
180 
181 20
182 3
183 2
184 3
185 14
186 
187 */
View Code

 

转载于:https://www.cnblogs.com/wuenze/p/8581988.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值