ZOJ 3494 BCD Code AC自动机 + 数位DP
来源:互联网 发布:网络歌手东篱照片 编辑:程序博客网 时间:2024/04/30 11:23
题目大意:
就是现在有0~9对应的BCD码(就是对应的4位的二进制), 然后给出了n串(0 <= n <= 1000)不能出现的串(只包含0和1), 现在问在数字A到B之间有多少个数在转换成BCD码表示之后不包含这n个串,1 <= A <= B <= 10^200
大致思路:
首先考虑到A和B的范围,暴力是不可行的,这里需要用到数位DP,也就是逐位确定的思想,首先用AC自动机算出从状态i出发下一位是0~9分别可到达的状态,或者不可走,然后逐位确定各个数位的值即可,记忆化搜索提高效率,启示这个数位DP还是挺简单的,注意前导零的问题即可。
代码如下:
Result : Accepted Memory : 3676 KB Time : 210 ms
/* * Author: Gatevin * Created Time: 2014/11/27 10:18:15 * File Name: Kagome.cpp */#include<iostream>#include<sstream>#include<fstream>#include<vector>#include<list>#include<deque>#include<queue>#include<stack>#include<map>#include<set>#include<bitset>#include<algorithm>#include<cstdio>#include<cstdlib>#include<cstring>#include<cctype>#include<cmath>#include<ctime>#include<iomanip>using namespace std;const double eps(1e-8);typedef long long lint;const lint mod = 1000000009LL;int n;char num[210];int bit[210];int bcd[2010][10];//bcd[i][j]表示从AC自动机的i状态,下一个数字是j会到达的状态,为-1表示不可走lint dp[2010][210];//dp[i][j]表示从AC自动机的i状态,接下来的j位没有边界和前导零限制时的方案数,用于记忆化struct Trie{ int next[2010][2], fail[2010]; bool end[2010]; int L, root; int newnode() { for(int i = 0; i < 2; i++) next[L][i] = -1; end[L++] = 0; return L - 1; } void init() { L = 0; root = newnode(); return; } void insert(char *s) { int now = root; for(; *s; s++) { if(next[now][*s - '0'] == -1) next[now][*s - '0'] = newnode(); now = next[now][*s - '0']; } end[now] = 1; return; } void build() { queue <int> Q; fail[root] = root; Q.push(root); while(!Q.empty()) { int now = Q.front(); Q.pop(); if(end[fail[now]]) end[now] = 1;//标记所有不能到达的点 for(int i = 0; i < 2; i++) if(next[now][i] == -1) next[now][i] = now == root ? root : next[fail[now]][i]; else { fail[next[now][i]] = now == root ? root : next[fail[now]][i]; Q.push(next[now][i]); } } return; }};Trie AC;void BCD()//计算bcd数组{ for(int i = 0; i < AC.L; i++) for(int j = 0; j < 10; j++) { bcd[i][j] = i; if(AC.end[bcd[i][j]]) { bcd[i][j] = -1; continue; } for(int k = 3; k >= 0; k--) { if(j & (1 << k)) bcd[i][j] = AC.next[bcd[i][j]][1]; else bcd[i][j] = AC.next[bcd[i][j]][0]; if(AC.end[bcd[i][j]]) { bcd[i][j] = -1; break; } } } return;}/* * 返回剩下的长度为L需要填充,当前在AC自动机的pos状态, * bound表示接下来这一位是否考虑上界bit限制 * lead表示之前是否已经有数字,即当前位填0是否有状态转移(前导零不转移) */lint dfs(int L, int pos, bool bound, bool lead)//写的不怎么优美....{ if(L == 0 && lead) return 1; if(L == 0 && !lead) return 0; if(dp[pos][L] != -1 && !bound && lead) return dp[pos][L]; lint ret = 0; if(!lead)//没有前导零 { ret = (ret + dfs(L - 1, pos, false, false)) % mod;//继续没有前导零 if(bound)//有上界限制 { for(int i = 1; i < bit[L]; i++)//接下来填的数比当前位小,接下去就没有限制 if(bcd[pos][i] != -1) ret = (ret + dfs(L - 1, bcd[pos][i], false, true)) % mod; if(bcd[pos][bit[L]] != -1) ret = (ret + dfs(L - 1, bcd[pos][bit[L]], true, true)) % mod;//有限制 } else for(int i = 1; i < 10; i++)//没有限制就随便填了,接下去也没有限制 if(bcd[pos][i] != -1) ret = (ret + dfs(L - 1, bcd[pos][i], false, true)) % mod; } else { if(bound) { for(int i = 0; i < bit[L]; i++) if(bcd[pos][i] != -1) ret = (ret + dfs(L - 1, bcd[pos][i], false, true)) % mod; if(bcd[pos][bit[L]] != -1) ret = (ret + dfs(L - 1, bcd[pos][bit[L]], true, true)) % mod; } else for(int i = 0; i < 10; i++) if(bcd[pos][i] != -1) ret = (ret + dfs(L - 1, bcd[pos][i], false, true)) % mod; } if(lead && dp[pos][L] == -1 && !bound) dp[pos][L] = ret;//记忆化 return ret;}//计算从0到当前数字有多少是不包含那N个危险串的lint solve(int len){ if(len == 0) return 0; for(int i = 0; i < len; i++) bit[len - i] = num[i] - '0'; return dfs(len, AC.root, true, false);}int main(){ int t; char ts[22]; scanf("%d", &t); while(t--) { scanf("%d", &n); AC.init(); while(n--) { scanf("%s", ts); AC.insert(ts); } AC.build(); BCD(); scanf("%s", num); int len = strlen(num); /* * 将当前的左界减去1 */ if(num[len - 1] > '0') { int tmp = len - 1; if(len == 1 && num[len - 1] == '1') len = 0; num[tmp] = num[tmp] - 1; } else { num[len - 1] = '9'; int now = len - 2; while(num[now] == '0') { num[now] = '9'; now--; } if(num[now] == '1' && now == 0) { for(int i = 0; i < len; i++) num[i] = num[i + 1]; len--; } else num[now] = num[now] - 1; } memset(dp, -1, sizeof(dp)); lint A = solve(len); scanf("%s", num); len = strlen(num); lint B = solve(len); printf("%lld\n", (B - A + mod) % mod); } return 0;}
0 0
- ZOJ 3494 BCD Code(AC自动机+数位DP)
- ZOJ 3494 BCD Code 数位DP+AC自动机
- ZOJ 3494 BCD Code (AC自动机+数位DP,5级)
- 【MZ】ZOJ 3494 BCD Code AC自动机+数位DP
- ZOJ 3494 BCD Code(AC自动机+数位DP)
- ZOJ 3494 BCD Code(AC自动机+数位DP)
- ZOJ 3494 BCD Code AC自动机+数位DP
- ZOJ 3494 BCD Code AC自动机+数位DP
- 【ZOJ】3494 BCD Code AC自动机+数位DP
- zoj 3494 BCD Code AC自动机+数位dp
- zoj 3494 BCD Code(AC自动机+数位dp)
- ZOJ 3494 BCD Code AC自动机 + 数位DP
- 【AC自动机+数位DP】【zoj 3494】BCD Code
- ZOJ Problem Set - 3494 BCD Code AC自动机+数位DP
- zoj 3494 BCD Code (ac自动机+数位dp)
- [AC自动机+数位dp] zoj 3494 BCD Code
- ZOJ - 3494 BCD Code(AC自动机+数位DP)
- ZOJ 3494 BCD Code (AC自动机 + 数位DP)
- 卖饼人生
- 今天写下文字,明天造就传奇。
- 关于51单片机的一些基本知识
- FreeMarker之概念介绍(一)
- Window XP驱动开发(二十)Window驱动的内存管理
- ZOJ 3494 BCD Code AC自动机 + 数位DP
- Android ART运行时无缝替换Dalvik虚拟机的过程分析
- MFC窗口创建、销毁消息流程
- JSP表单填写验证---JSP+JavaBean
- 栈帧&溢出
- VIM中文乱码原理详解及终极解决方案[经典]
- 摸索:opencv結合socket的網絡圖像傳輸
- context
- 11月24号-11月30号