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
",(ans[1]+mod-ans[0])%mod);
158     }
159     return 0;
160 }
AC代码
原文地址:https://www.cnblogs.com/xcw0754/p/4857334.html