• 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代码
  • 相关阅读:
    Python 描述符(descriptor) 杂记
    Celery 使用简介
    异步任务神器 Celery 简明笔记
    高性能框架gevent和gunicorn在web上的应用及性能测试
    Flask + Gunicorn + Nginx 部署
    Mysql查看最大连接数和修改最大连接数
    配置 influxDB 鉴权及 HTTP API 写数据的方法
    Java 字符串拼接 五种方法的性能比较分析 从执行100次到90万次
    linux端口开放指定端口的两种方法
    java自带的监控工具VisualVM一
  • 原文地址:https://www.cnblogs.com/xcw0754/p/4857334.html
Copyright © 2020-2023  润新知