ZOJ 3494 BCD Code (数位DP,AC自动机)

题意:

  将一个整数表示成4个bit的bcd码就成了一个01串,如果该串中出现了部分病毒串,则是危险的。给出n个病毒串(n<=100,长度<21),问区间[L,R]中有几个数字是不含病毒串的(结果需要取模)?(0<L<=R<=10200

思路:

  区间非常大,怎样暴力统计都是不科学的。首先确定状态,按传统,一维必定是位数,二维就是压缩的状态了,如果长度为20个bit的话,200*104万的数组是不行的。类似多模式串匹配问题,病毒串可以构建成AC自动机,那么每个点可以代表一个独立状态,而n<=100,所以最多20n个节点,是可以的。转移的话可以根据新考虑的数位是多少,然后在AC自动机上面走4步(BCD码是4bit)到达另一个状态(点),如果经过了病毒串的末尾节点,表示该数出现病毒串,就不能转移。这个可以在AC自动机创建完成后,预处理出来就行了。而对于每个询问[L,R],L仍然是需要减1的,大数减1比较简单。注意点是,AC自动机上的tag需要特殊处理,如果有病毒串"asdf"和串"sd",而碰到原串为"asdd",别忘了还有"sd"。这只需要在构建fail指针的时候处理一下。

  1 #include <bits/stdc++.h>
  2 #include <iostream>
  3 #include <cstdio>
  4 #include <cstring>
  5 #include <cmath>
  6 #include <map>
  7 #include <algorithm>
  8 #include <vector>
  9 #include <iostream>
 10 #define pii pair<int,int>
 11 #define INF 0x7f3f3f3f
 12 #define LL long long
 13 #define ULL unsigned long long
 14 using namespace std;
 15 const double PI  = acos(-1.0);
 16 const int N=210;
 17 const LL mod=1000000009;
 18
 19 struct Trie{
 20     static const int NN=2100;    //节点数
 21     static const int CC=2;    //孩子数
 22     int next[NN][CC], fail[NN];
 23     bool tag[NN];
 24     int root, node_cnt;
 25     int newnode(){
 26         for(int i=0; i<CC; i++)    next[node_cnt][i]=-1;
 27         tag[node_cnt]=false;    //刚创建时,默认非叶子
 28         return node_cnt++;
 29     }
 30     void init(){
 31         node_cnt=0;
 32         root=newnode(); //root是虚拟点
 33     }
 34     void insert(char s[]){
 35         int len=strlen(s);
 36         int now=root;
 37         for(int i=0; i<len; i++){
 38             if(next[now][s[i]-‘0‘]==-1)
 39                 next[now][s[i]-‘0‘]=newnode();
 40             now=next[now][s[i]-‘0‘];
 41         }
 42         tag[now]=true; //尾节点:可能有多个相同模式串!
 43     }
 44     void buildAC(){
 45         fail[root]=root; queue<int> que;
 46         for(int i=0; i<CC; i++){
 47             if(next[root][i]==-1)
 48                 next[root][i]=root;
 49             else{
 50                 fail[next[root][i]]=root;
 51                 que.push(next[root][i]);
 52             }
 53         }
 54         while(!que.empty()){
 55             int now=que.front();que.pop();
 56             if(tag[fail[now]]==true) tag[now]=true;   //注意
 57             for(int i=0; i<CC; i++){
 58                 if( next[now][i]==-1)
 59                     next[now][i]=next[fail[now]][i];
 60                 else{
 61                     fail[next[now][i]]=next[fail[now]][i];
 62                     que.push(next[now][i]);
 63                 }
 64             }
 65         }
 66     }
 67 }AC;
 68
 69 LL f[N][N*10];
 70 int bcd[N*10][10], len;
 71 char bit[N];
 72
 73 LL dfs(int i,int s,int sum,bool e)  //s是节点编号
 74 {
 75     if(i==0)         return 1;
 76     if(!e&&~f[i][s]) return f[i][s];
 77
 78     LL ans=0;
 79     if(sum==0)  //处理前缀0
 80     {
 81         ans+=dfs(i-1, s, 0, e&&bit[i]==‘0‘);
 82         ans%=mod;
 83     }
 84
 85     int d= sum>0? 0: 1;     //起
 86     int u= e? bit[i]-‘0‘: 9;//终
 87     for(; d<=u; d++)
 88     {
 89         if(bcd[s][d]!=-1)
 90         {
 91             ans+=dfs(i-1, bcd[s][d], sum+d, e&&d==u);
 92             ans%=mod;
 93         }
 94     }
 95     if(!e&&sum) f[i][s]=ans;    //没有前导零
 96     return ans;
 97 }
 98
 99
100 LL cal()
101 {
102     reverse(bit+1, bit+len+1);
103     if(len==1&&bit[len]==‘0‘)   return 1;
104     return dfs(len, 0, 0, true);
105 }
106
107 int changeto(int s,int t)
108 {
109     if(AC.tag[s])   return -1;  //已经是病毒串
110     int now=s;
111     for(int i=3; i>=0; i--)
112     {
113         if( AC.tag[AC.next[now][(t>>i)&1]]==1 ) return -1; //病毒串
114         now=AC.next[now][(t>>i)&1];
115     }
116     return now;
117 }
118 void pre_cal()  //预处理转移
119 {
120     for(int i=0; i<AC.node_cnt; i++)
121         for(int j=0; j<10; j++)
122             bcd[i][j]=changeto(i,j);
123 }
124 int main()
125 {
126     freopen("input.txt","r",stdin);
127     int t, n;cin>>t;
128     LL  ans[2];
129     while( t-- )
130     {
131         memset(bcd, -1, sizeof(bcd));
132         memset(f, -1, sizeof(f));
133         AC.init();
134         scanf("%d",&n);
135         for(int i=0; i<n; i++)
136         {
137             scanf("%s",bit);
138             AC.insert(bit);
139         }
140         AC.buildAC();  //AC自动机
141         pre_cal();     //预处理转移
142
143         for(int j=0; j<2; j++)
144         {
145             scanf("%s", bit+1);
146             len=strlen(bit+1);
147             if(j==0)
148             {
149                 for(int i=len; i>0; i--)    //注意逆序
150                 {
151                     if( bit[i]>‘0‘ ){bit[i]--;break;}
152                     else            bit[i]=‘9‘;
153                 }
154             }
155             ans[j]=cal();
156         }
157         printf("%lld\n",(ans[1]+mod-ans[0])%mod);
158     }
159     return 0;
160 }

AC代码

时间: 2024-12-23 22:18:55

ZOJ 3494 BCD Code (数位DP,AC自动机)的相关文章

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个字符,中途有经过禁止点都是不行

ZOJ 3494 BCD Code (AC自己主动机 + 数位DP)

题目链接:BCD Code 解析:n个病毒串.问给定区间上有多少个转换成BCD码后不包括病毒串的数. 很奇妙的题目. . 经典的 AC自己主动机 + 数位DP 的题目. 首先使用AC自己主动机,得到bcd[i][j]表示状态i,加了数字j以后到达的状态.为-1表示不能转移 然后就是数位DP了 注意记录为0的状态 AC代码: #include <cstdio> #include <iostream> #include <cstring> #include <algo

FZU 2113 BCD Code 数位dp

数位dp,但是很奇怪的是我在虚拟oj上用GUC C++提交会wa,用Visual c++提交正确,但是加上注释后提交又莫名CE……好任性啊 0 ,0 题目思路:看代码吧 注释很详细 #include<iostream>#include<algorithm>#include<cstring>#include<vector>#include<stdio.h>#include<stdlib.h>#include<queue>#i

zoj 3430 Detect the Virus(AC自动机)

Detect the Virus Time Limit: 2 Seconds      Memory Limit: 65536 KB One day, Nobita found that his computer is extremely slow. After several hours' work, he finally found that it was a virus that made his poor computer slow and the virus was activated

ZOJ 2619 Generator (概率、AC自动机、高斯消元)

Generator 题目:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2619 题意:给定一个数N,代表可以选前N个字母.然后给定一个仅有前N个字母组成的字符串,问从空串开始构造,每次可以在已有基础上从前N个字母中挑选一个加在后面,问构造的字符串的长度期望是多少? 思路:如果给定的串长度为L,那么对于构造的串,对应的状态就是0到L之间的数.如果为L即为构造成功.记F[i] 为从i状态到达L状态期望的步数,那么对于0到L

POJ 1850 Code 数位DP

据说又是一道组合数学题,数学不好的我只想出的DP写法 注意如果输入不合法要输出0 #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #include <cstdli

[后缀数组+dp/AC自动机+dp+线段树] hdu 4117 GRE Words

题意: 给你N个字符串, N(1 <= N <= 2w), 所有串的长度加一起不超过30w.每个串有个值.这个值[-1000, 1000]. 问不打乱字符串顺序,从中取若干个字符串,使得前一个串是后一个串的子串,求满足前面调条件的字符串值得和最大,求这个值. 思路: 其实就是一个很明显的dp. dp[i]代表以第i个字符串结尾的最大权值. 但是就是子串这个问题怎么处理. 由于这题数据比较水可以用后缀数组处理这个问题. 将所有字符串拼接,做sa. 每次在height数组里往上和往下寻找公共前缀等

ZOJ 3430 Detect the Virus (AC自动机)

题目链接:Detect the Virus 题意:n个模式串,一个文本串,问文本串包含几个模式串. 解析:解码 + AC自动机. 解码过程:先将字符串转换成ASCII 然后根据相应的ASCII 转换成二进制,每一个是6位,不够加0,然后取8位为一个字符,求得的字符串为要的字符串. PS:注意sigma_size = 256 AC代码: #include <bits/stdc++.h> using namespace std; struct Trie{ int next[520*64][256]

Zoj 3535 Gao the String II (AC自动机+dp)

题目大意: 用集合A中的串构造出一个串,使之让更多的setB中的串成为他的子串. 思路分析: 和 Codeforces 86C 差不多. 不过这里是要用A中的构造. 先用A 和 B的串构造一个自动机.然后对于A集合的尾结点给出一个最大后缀匹配,对于B集合的尾结点给一个权值. dp[i][j][k] 表示已经构造出来了一个长度为i的串,现在走到了自动机的j结点,i长度后面有k个字符是没有匹配到的. 继续在自动机上走进行状态转移. if(isword >= k +1 )dp [i+1] [j->n