zoj3494BCD Code(ac自动机+数位dp)

l链接

这题想了好一会呢。。刚开始想错了,以为用自动机预处理出k长度可以包含的合法的数的个数,然后再数位dp一下就行了,写到一半发现不对,还要处理当前走的时候是不是为合法的,这一点无法移到trie树上去判断。

之后想到应该在trie树上进行数位dp,走到第i个节点且长度为j的状态是确定的,所以可以根据trie树上的节点来进行确定状态。

dp[i][j]表示当前节点为i,数第j位时可以包含多少个合法的数。

  1 #include <iostream>
2 #include<cstdio>
3 #include<cstring>
4 #include<string>
5 #include<algorithm>
6 #include<stdlib.h>
7 #include<vector>
8 #include<cmath>
9 #include<queue>
10 #include<set>
11 using namespace std;
12 #define N 2010
13 #define LL long long
14 #define INF 0xfffffff
15 const double eps = 1e-8;
16 const double pi = acos(-1.0);
17 const double inf = ~0u>>2;
18 const int child_num = 2;
19 const int mod = 1000000009;
20 int dp[210][N];
21 char s1[210],s2[210];
22 class AC
23 {
24 private:
25 int ch[N][child_num];
26 int Q[N];
27 int fail[N];
28 int val[N];
29 int id[127];
30 int sz;
31 int dd[810][N];
32 public:
33 void init()
34 {
35 fail[0] = 0;
36 id[‘0‘] = 0;id[‘1‘] = 1;
37 }
38 void reset()
39 {
40 memset(val,0,sizeof(val));
41 memset(ch[0],0,sizeof(ch[0]));
42 sz=1;
43 }
44 void insert(char *a,int key)
45 {
46 int p =0 ;
47 for( ; *a ; a++)
48 {
49 int d = id[*a];
50 if(ch[p][d]==0){
51 memset(ch[sz],0,sizeof(ch[sz]));
52 ch[p][d] = sz++;
53 }
54 p = ch[p][d];
55 }
56 val[p] = key;
57 }
58 void construct()
59 {
60 int i,head=0,tail = 0;
61 for(i = 0 ;i < child_num ; i++)
62 {
63 if(ch[0][i])
64 {
65 fail[ch[0][i]] = 0;
66 Q[tail++] = ch[0][i];
67 }
68 }
69 while(head!=tail)
70 {
71 int u = Q[head++];
72 val[u]|=val[fail[u]];
73 for(i =0 ;i < child_num ; i++)
74 {
75 if(ch[u][i])
76 {
77 fail[ch[u][i]] = ch[fail[u]][i];
78 Q[tail++] = ch[u][i];
79 }
80 else ch[u][i] = ch[fail[u]][i];
81 }
82 }
83 }
84 int dfs(char *s,int i,int c,int e,int k)
85 {
86 if(i==-1)
87 {
88 return 1;
89 }
90 if(!e&&~dp[i][c])
91 {
92 return dp[i][c];
93 }
94 int mk = e?s[i]-‘0‘:9;
95 int ans = 0;
96 for(int j = 0; j <= mk ; j++)
97 {
98 if(!k&&j==0&&i)
99 {
100 ans = (ans+dfs(s,i-1,c,e&&j==mk,k));
101 continue;
102 }
103 int p = c,flag = 1;
104 for(int g = 3; g >=0 ; g--)
105 {
106 int o = (j&(1<<g))?1:0;
107 p = ch[p][o];
108 int tmp = p;
109 while(tmp!=0)
110 {
111 if(val[tmp])
112 {
113 flag = 0;
114 break;
115 }
116 tmp = fail[tmp];
117 }
118 if(!flag) break;
119 }
120 if(flag)
121 {
122 ans = (ans+dfs(s,i-1,p,e&&j==mk,1))%mod;
123 }
124 }
125 return e?ans:dp[i][c] = ans;
126 }
127 void work(char *s1,char *s2)
128 {
129 memset(dp,-1,sizeof(dp));
130 printf("%d\n",(dfs(s2,strlen(s2)-1,0,1,0)-dfs(s1,strlen(s1)-1,0,1,0)+mod)%mod);
131 }
132 }ac;
133 char vir[22];
134 char ss1[210],ss2[210];
135 int main()
136 {
137 int t,n,i;
138 ac.init();
139 scanf("%d",&t);
140 while(t--)
141 {
142 ac.reset();
143 scanf("%d",&n);
144 while(n--)
145 {
146 scanf("%s",vir);
147 ac.insert(vir,1);
148 }
149 ac.construct();
150 scanf("%s%s",s1,s2);
151 int k = strlen(s1),kk= strlen(s2);
152 for(i = k-1 ; i >= 0; i--)
153 {
154 if(s1[i]>‘0‘)
155 {
156 s1[i]-=1;
157 break;
158 }
159 else
160 s1[i] = ‘9‘;
161 }
162 for(i = 0; i < k ; i++)
163 ss1[k-1-i] = s1[i];
164 ss1[k] = ‘\0‘;
165 for(i = 0; i < kk ; i++)
166 ss2[kk-1-i] = s2[i];
167 ss2[kk] = ‘\0‘;
168 ac.work(ss1,ss2);
169 }
170 return 0;
171 }

时间: 2025-01-16 09:03:12

zoj3494BCD Code(ac自动机+数位dp)的相关文章

zoj 3494 BCD Code(AC自动机+数位dp)

题目链接:zoj 3494 BCD Code 题目大意:给定n个2进制串,然后有一个区间l,r,问说l,r之间有多少个数转换成BCD二进制后不包含上面的2进制串. 解题思路:AC自动机+数位dp.先对禁止串建立AC自动机,所有的单词节点即为禁止通行的节点.接着进行数位dp, 用solve(r) - solve(l-1), 这里的l需要用到大数减法.dp[i][j]表示第i为移动到节点j的可行方案数,每次枚举下一位数字,因 为是BCD二进制,所以每位数要一次性移动4个字符,中途有经过禁止点都是不行

HDU 4518 ac自动机+数位dp

吉哥系列故事--最终数 Time Limit: 500/200 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Total Submission(s): 304    Accepted Submission(s): 102 Problem Description 在2012年腾讯编程马拉松比赛中,吉哥解决了一道关于斐波那契的题目,这让他非常高兴,也更加燃起了它对数学特别是斐波那契数的热爱.现在,它又在思考一个关于斐波那契

【bzoj3530】[Sdoi2014]数数 AC自动机+数位dp

题目描述 我们称一个正整数N是幸运数,当且仅当它的十进制表示中不包含数字串集合S中任意一个元素作为其子串.例如当S=(22,333,0233)时,233是幸运数,2333.20233.3223不是幸运数.给定N和S,计算不大于N的幸运数个数. 输入 输入的第一行包含整数N.接下来一行一个整数M,表示S中元素的数量.接下来M行,每行一个数字串,表示S中的一个元素. 输出 输出一行一个整数,表示答案模109+7的值. 样例输入 20 3 2 3 14 样例输出 14 题解 AC自动机+数位dp 同学

[JSOI2007]文本生成器 [AC自动机,dp]

时刻要记住正难则反,可以知道总数是 \(26^m\),我们可以减掉不合法的. AC自动机上面dp,不合法的显然就是没有出现任意的一个串,根据rainy的教导 单词 \(b,bce,abcd\) 的 ACAM 然后 \(dp\) 就好了,由于点数不超过 \(n*m \leq 6000\),然后你每一位枚举复杂度是 \(m^2n\) 的 可以通过本题 // powered by c++11 // by Isaunoya #include <bits/stdc++.h> #define rep(i,

POJ 3691 DNA repair 基于AC自动机的DP

dp[i][j] 表示长度为 i 的前缀到达第 j 个节点的最小更改数目. 很显然有dp[0][0] = 0; dp[ i ][ j ] = min(dp[ i ][ j ],dp[i-1][k] + (j == k ? 0 : 1)),当且仅当j,k满足下列条件时. j 不为某条模式串的末节点 且 j 到 root 的由失败指针组成的路径上无末节点. j 是k的儿子节点 或者 j 的父节点可由 k 沿着失败指针找到. #include <algorithm> #include <ios

[AC自动机+概率dp] hdu 3689 Infinite monkey theorem

题意: 给n个字母,和m次数. 然后输入n个字母出现的概率 然后再给一个目标串str 然后问m次中敲出目标串的概率是多少. 思路: AC自动机+概率dp的简单题. 首先建立trie图,然后就是状态转移了 dp版本: dp三重循环变量次数,节点数,和字母数 代码: #include"cstdlib" #include"cstdio" #include"cstring" #include"cmath" #include"

HDU 2825 Wireless Password (AC自动机,DP)

http://acm.hdu.edu.cn/showproblem.php?pid=2825 Wireless Password Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 4560    Accepted Submission(s): 1381 Problem Description Liyuan lives in a old a

POJ 3691 &amp; HDU 2457 DNA repair (AC自动机,DP)

http://poj.org/problem?id=3691 http://acm.hdu.edu.cn/showproblem.php?pid=2457 DNA repair Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 5690   Accepted: 2669 Description Biologists finally invent techniques of repairing DNA that contain

POJ 1625 Censored!(AC自动机,DP)

http://poj.org/problem?id=1625 Censored! Time Limit: 5000MS   Memory Limit: 10000K Total Submissions: 8266   Accepted: 2229 Description The alphabet of Freeland consists of exactly N letters. Each sentence of Freeland language (also known as Freish)