• AC自动机入门


    Aho-Corasick automaton,该算法在1975年产生于贝尔实验室,是著名的多模式匹配算法之一。

    KMP算法很好的解决了单模式匹配问题,如果有了字典树的基础,我们可以完美的结合二者解决多模式匹配问题。

    在KMP算法中,我们预先根据待匹配串自身的信息得到失配指针,使得在每次匹配不成功后,可以不再去处理模式串的已匹配过的部分,进而使得复杂度降为O(N)。

    对于多模式串匹配问题,当一个模式串与待匹配串不匹配时,失配指针可以指向任意一个串,这就需要我们利用字典树来组织所有模式串并得到失配指针。

    构造失配指针的详细过程可参考 http://blog.csdn.net/niushuai666/article/details/7002823

    时间复杂度分析:假设有N个长不超过m的模式串,待匹配串长度为L。建字典树的复杂度为O(Nm),对于每个L的前缀,最坏情况下匹配树高(m)次,故总的复杂度为O((N+L)*m)。

    我的模板

     1 /*
     2 基于HDOJ 2222 的 AC自动机
     3 文本串对多个模板串的查找
     4 */
     5 #include<bits/stdc++.h>
     6 using namespace std;
     7 const int maxn = 555555;
     8 const int alp =26;
     9 
    10 int ch[maxn][alp], ed[maxn], fail[maxn];
    11 char str[1001000];
    12 
    13 int root, sz;
    14 
    15 int newnode()
    16 {
    17     memset(ch[sz], -1, sizeof(ch[sz]));
    18     ed[sz] = 0;
    19     return sz++;
    20 }
    21 void init()
    22 {
    23     sz = 0;
    24     root = newnode();
    25 }
    26 void Insert(char str[])
    27 {
    28     int len = strlen(str);
    29     int now = root;
    30     for(int i=0; i<len; i++)
    31     {
    32         int &tmp = ch[now][str[i]-'a'];
    33         if(tmp == -1) tmp = newnode();
    34         now = tmp;
    35     }
    36     ed[now] ++;
    37 }
    38 
    39 
    40 void build()
    41 {
    42     queue<int> q;
    43     fail[root] = root;
    44     for(int i=0; i<alp; i++)
    45     {
    46         int &tmp = ch[root][i];
    47         if(tmp == -1) tmp = root;
    48         else fail[tmp] = root, q.push(tmp);
    49     }
    50     while(!q.empty())
    51     {
    52         int now = q.front(); q.pop();
    53         for(int i=0; i<alp; i++)
    54         {
    55             int &tmp = ch[now][i];
    56             if(tmp == -1) tmp = ch[fail[now]][i];
    57             else fail[tmp] = ch[fail[now]][i], q.push(tmp);
    58         }
    59     }
    60 }
    61 
    62 int query(char str[])
    63 {
    64     int len = strlen(str);
    65     int now = root;
    66     int ret = 0;
    67     for(int i=0; i<len; i++)
    68     {
    69         int tmp = now = ch[now][str[i]-'a'];
    70         while(tmp != root)
    71         {
    72             if(ed[tmp] > 0)
    73                 ret += ed[tmp],
    74                 ed[tmp] = 0;
    75             tmp = fail[tmp];
    76         }
    77     }
    78     return ret;
    79 }
    80 
    81 int main()
    82 {
    83     int n, t;
    84     scanf("%d",&t);
    85     while(t--)
    86     {
    87         scanf("%d",&n);
    88         init();
    89         for(int i=1; i<=n; i++)
    90             scanf("%s",str), Insert(str);
    91         build();
    92         scanf("%s",str);
    93         printf("%d
    ",query(str));
    94     }
    95     return 0;
    96 }
    View Code

    入门练习题  http://acm.hust.edu.cn/vjudge/contest/view.action?cid=96376#overview

    1. hdu 2896  病毒侵袭

       字典树的节点标记模式串的编号即可,注意字符的ASCALL码在128以内。

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 const int maxn = 111111;
      4 const int alp = 128;
      5 
      6 int ch[maxn][alp], ed[maxn], fail[maxn];
      7 char str[10100];
      8 
      9 int root, sz;
     10 
     11 int newnode()
     12 {
     13     memset(ch[sz], -1, sizeof(ch[sz]));
     14     ed[sz] = 0;
     15     return sz++;
     16 }
     17 void init()
     18 {
     19     sz = 0;
     20     root = newnode();
     21 }
     22 void Insert(char str[], int tag)
     23 {
     24     int len = strlen(str);
     25     int now = root;
     26     for(int i=0; i<len; i++)
     27     {
     28         int &tmp = ch[now][str[i]];
     29         if(tmp == -1) tmp = newnode();
     30         now = tmp;
     31     }
     32     ed[now] = tag;
     33 }
     34 
     35 void build()
     36 {
     37     queue<int> q;
     38     fail[root] = root;
     39     for(int i=0; i<alp; i++)
     40     {
     41         int &tmp = ch[root][i];
     42         if(tmp == -1) tmp = root;
     43         else fail[tmp] = root, q.push(tmp);
     44     }
     45     while(!q.empty())
     46     {
     47         int now = q.front(); q.pop();
     48         for(int i=0; i<alp; i++)
     49         {
     50             int &tmp = ch[now][i];
     51             if(tmp == -1) tmp = ch[fail[now]][i];
     52             else fail[tmp] = ch[fail[now]][i], q.push(tmp);
     53         }
     54     }
     55 }
     56 
     57 bool vis[510];
     58 
     59 void query(char str[])
     60 {
     61     int len = strlen(str);
     62     int now = root;
     63     int ret = 0;
     64     for(int i=0; i<len; i++)
     65     {
     66         int tmp = now = ch[now][str[i]];
     67         while(tmp != root)
     68         {
     69             if(ed[tmp]) vis[ed[tmp]] = 1;
     70             tmp = fail[tmp];
     71         }
     72     }
     73 }
     74 
     75 int main()
     76 {
     77     int n, m;
     78     while(scanf("%d",&n) == 1)
     79     {
     80         init();
     81         for(int i=1; i<=n; i++)
     82             scanf("%s",str), Insert(str, i);
     83         build();
     84         scanf("%d",&m);
     85         int total = 0;
     86         for(int i=1; i<=m; i++)
     87         {
     88             memset(vis, 0, sizeof(vis));
     89             scanf("%s",str);
     90             query(str);
     91             int a[5], id = 0;
     92             for(int j=1; j<=n; j++)
     93                 if(vis[j]) a[++id] = j;
     94             if(id) {
     95                 printf("web %d:",i);
     96                 for(int j=1; j<=id; j++) printf(" %d", a[j]);
     97                 puts("");
     98                 total++;
     99             }
    100         }
    101         printf("total: %d
    ",total);
    102     }
    103     return 0;
    104 }
    View Code

    2. hdu 3065  病毒侵袭持续中

      由于所找的串允许重叠,那就恰好是AC自动机模板了,在Trie上查找时,只要该节点有标记,加上即可。待匹配串的除大写字母以外的可见字符是没有用的,统一更改为'Z'+1就行了。 
     
     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int maxn = 111111;
     4 const int alp = 27;
     5 
     6 int ch[maxn][alp], ed[maxn], fail[maxn];
     7 char str[2000100];
     8 int num[1010];
     9 
    10 int root, sz;
    11 
    12 int newnode()
    13 {
    14     memset(ch[sz], -1, sizeof(ch[sz]));
    15     ed[sz] = 0;
    16     return sz++;
    17 }
    18 void init()
    19 {
    20     sz = 0;
    21     root = newnode();
    22 }
    23 void Insert(char str[], int tag)
    24 {
    25     int len = strlen(str);
    26     int now = root;
    27     for(int i=0; i<len; i++)
    28     {
    29         int &tmp = ch[now][str[i]-'A'];
    30         if(tmp == -1) tmp = newnode();
    31         now = tmp;
    32     }
    33     ed[now] = tag;
    34 }
    35 
    36 void build()
    37 {
    38     queue<int> q;
    39     fail[root] = root;
    40     for(int i=0; i<alp; i++)
    41     {
    42         int &tmp = ch[root][i];
    43         if(tmp == -1) tmp = root;
    44         else fail[tmp] = root, q.push(tmp);
    45     }
    46     while(!q.empty())
    47     {
    48         int now = q.front(); q.pop();
    49         for(int i=0; i<alp; i++)
    50         {
    51             int &tmp = ch[now][i];
    52             if(tmp == -1) tmp = ch[fail[now]][i];
    53             else fail[tmp] = ch[fail[now]][i], q.push(tmp);
    54         }
    55     }
    56 }
    57 
    58 void query(char str[])
    59 {
    60     int len = strlen(str);
    61     int now = root;
    62     int ret = 0;
    63     for(int i=0; i<len; i++)
    64     {
    65         int tmp = now = ch[now][str[i]-'A'];
    66         while(tmp != root)
    67         {
    68             if(ed[tmp])  num[ed[tmp]] ++;
    69             tmp = fail[tmp];
    70         }
    71     }
    72 }
    73 char s[1010][55];
    74 int main()
    75 {
    76     int n, m;
    77     while(scanf("%d",&n) == 1)
    78     {
    79         init();
    80         for(int i=1; i<=n; i++)
    81             scanf("%s",s[i]), Insert(s[i], i), num[i]=0;
    82         build();
    83         scanf("%s",str);
    84         for(int i=0; str[i]; i++)
    85             if(str[i]<'A' || str[i] >'Z') str[i] = 'Z'+1;
    86         query(str);
    87         for(int i=1; i<=n; i++)
    88             if(num[i])
    89             printf("%s: %d
    ",s[i], num[i]);
    90     }
    91     return 0;
    92 }
    View Code

    3. zoj 3430  Detect the Virus

       恶心+AC自动机,以后写
     
    4.poj 2778  DNA Sequence

      给N(N<=10)个长度不超过10的DNA串,问可以构造出多少个长度为M(M<=2000000000)的DNA串不含以上串,所有串仅由'A'、'C'、'G'、'T'四个字符构成。

    对于一个有向图,我们要求一点x到另一点y的长度为L不同路径的个数,构造一个矩阵,令mat[i][j]表示直接相连i和j的边的数量,那么该矩阵的L次幂的mat[x,y]就是答案。

      这里我们可以先构造出最多101个节点的Trie图,建立AC自动机,标记给出的串。对于每个节点u,并枚举每个以上字符,若该节点没被标记并且转移后的节点v也没有被标记,那么mat[u][v]++。求出pow(mat,M),矩阵第一行之和就是答案,因为它代表从root(空串)开始所有的长度为M的满足要求的所有串的数量和。

      *注意,对于串"A"和"TAG",在建立自动机时,对于串"TAG"中的'A'节点也应该被标记,也就是说,每个节点的标记要考虑到失配节点的标记。

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<cmath>
      5 #include<algorithm>
      6 #include<queue>
      7 typedef long long LL;
      8 using namespace std;
      9 const int maxn = 111111;
     10 const int N = 110;
     11 const int alp = 4;
     12 const LL mod = 100000;
     13 int ch[maxn][alp], ed[maxn], fail[maxn];
     14 char str[20];
     15 int Hash['z'];
     16 int root, sz;
     17 void mul(LL a[N][N], LL b[N][N])
     18 {
     19     LL c[N][N] = {0};
     20     for(int k=0; k<sz; k++)
     21         for(int i=0; i<sz; i++)
     22             for(int j=0; j<sz; j++)
     23             c[i][j] = (c[i][j] + a[i][k]*b[k][j]) % mod;
     24     memcpy(a, c, sizeof(c));
     25 }
     26 void quick_mod(LL a[N][N], int b)
     27 {
     28     LL c[N][N] = {0};
     29     for(int i=0; i<sz; i++) c[i][i] = 1;
     30     while(b)
     31     {
     32         if(b&1) mul(c,a);
     33         mul(a, a);
     34         b>>=1;
     35     }
     36     memcpy(a, c, sizeof(c));
     37 }
     38 
     39 int newnode()
     40 {
     41     memset(ch[sz], -1, sizeof(ch[sz]));
     42     ed[sz] = 0;
     43     return sz++;
     44 }
     45 void init()
     46 {
     47     sz = 0;
     48     root = newnode();
     49 }
     50 void Insert(char str[])
     51 {
     52     int len = strlen(str);
     53     int now = root;
     54     for(int i=0; i<len; i++)
     55     {
     56         int &tmp = ch[now][Hash[str[i]]];
     57         if(tmp == -1) tmp = newnode();
     58         now = tmp;
     59     }
     60     ed[now] = 1;
     61 }
     62 
     63 void build()
     64 {
     65     queue<int> q;
     66     fail[root] = root;
     67     for(int i=0; i<alp; i++)
     68     {
     69         int &tmp = ch[root][i];
     70         if(tmp == -1) tmp = root;
     71         else fail[tmp] = root, q.push(tmp);
     72     }
     73     while(!q.empty())
     74     {
     75         int now = q.front(); q.pop();
     76         for(int i=0; i<alp; i++)
     77         {
     78             int &tmp = ch[now][i];
     79             if(tmp == -1) tmp = ch[fail[now]][i];
     80             else fail[tmp] = ch[fail[now]][i], ed[tmp] |= ed[fail[tmp]], q.push(tmp);
     81         }
     82     }
     83 }
     84 
     85 void get_matrix(LL a[N][N])
     86 {
     87     for(int i=0; i<sz; i++)
     88     {
     89         if(ed[i]) continue;
     90         for(int j=0; j<4; j++)
     91         {
     92             if(ed[ch[i][j]]) continue;
     93             a[i][ch[i][j]] ++;
     94         }
     95     }
     96 }
     97 
     98 int main()
     99 {
    100     int n, m;
    101     Hash['A'] = 0, Hash['C'] = 1, Hash['G'] = 2, Hash['T'] = 3;
    102     while(scanf("%d %d",&n,&m) == 2)
    103     {
    104         init();
    105         for(int i=1; i<=n; i++)
    106         {
    107             scanf("%s",str);
    108             Insert(str);
    109         }
    110         build();
    111         LL a[N][N] = {0};
    112         get_matrix(a);
    113         quick_mod(a, m);
    114         LL ans = 0;
    115         for(int i=0; i<sz; i++)
    116             ans =(ans + a[0][i]) % mod;
    117         printf("%I64d
    ",ans);
    118     }
    119     return 0;
    120 }
    View Code

      

    5.hdu 2243  考研路茫茫——单词情节

      求长度不超过m的至少包含一个所给模式串的串的个数,并对2^64取模。

    • 假设长度只能为m,上题中我们计算了不包含模式串的串的个数,而串的总数为pow(26,m),包含模式串的串的个数就是两者只差,这是很容易得出的。
    • 那么如何求出所有长度不超过m的串的总数呢?
    • 对于前者,我们对上题的矩阵的边长加1,并且置最后一列全为1,这样,矩阵的第一行之和就记录了所有情况之和,由于初始第一行最后一列为1,结果减一即可。
    • 对于后者,令sum_pow(26,x)表示26^1+26^2+26^3+...+26^x, 则有,若x为奇数转化为26^x+sum_pow(26,x-1), 若为偶数则转化为sum_pow(26,x/2)*(26^(x/2)+1),这样可在log(x)内的时间复杂度内求出。
    • 对于模2^64,只要将所有结果保存为unsigned long long 型,自然溢出就相当于对2^64取模。
      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<cmath>
      5 #include<algorithm>
      6 #include<queue>
      7 using namespace std;
      8 typedef unsigned long long ull;
      9 const int N = 50;
     10 const int alp = 26;
     11 const int maxn = 555555;
     12 int ch[maxn][alp], ed[maxn], fail[maxn];
     13 int root, sz;
     14 
     15 int newnode()
     16 {
     17     memset(ch[sz], -1, sizeof(ch[sz]));
     18     ed[sz++] = 0;
     19     return sz-1;
     20 }
     21 void init()
     22 {
     23     sz = 0;
     24     root = newnode();
     25 }
     26 
     27 void Insert(char str[])
     28 {
     29     int len = strlen(str);
     30     int now = root;
     31     for(int i=0; i<len; i++)
     32     {
     33         int &tmp = ch[now][str[i]-'a'];
     34         if(tmp == -1) tmp = newnode();
     35         now = tmp;
     36     }
     37     ed[now] = 1;
     38 }
     39 
     40 void build()
     41 {
     42     queue<int> q;
     43     fail[root] = root;
     44     for(int i=0; i<alp; i++)
     45     {
     46         int &tmp = ch[root][i];
     47         if(tmp == -1) tmp = root;
     48         else fail[tmp] = root, q.push(tmp);
     49     }
     50     while(!q.empty())
     51     {
     52         int now = q.front(); q.pop();
     53         for(int i=0; i<alp; i++)
     54         {
     55             int &tmp = ch[now][i];
     56             if(tmp == -1) tmp = ch[fail[now]][i];
     57             else fail[tmp] = ch[fail[now]][i],
     58                  ed[tmp] |= ed[fail[tmp]],
     59                  q.push(tmp);
     60         }
     61     }
     62 }
     63 
     64 void get_matric(ull a[N][N])
     65 {
     66     for(int i=0; i<sz; i++)
     67     {
     68         if(ed[i]) continue;
     69         for(int j=0; j<alp; j++)
     70         {
     71             if(ed[ch[i][j]]) continue;
     72             a[i][ch[i][j]] ++;
     73         }
     74     }
     75     for(int i=0; i<=sz; i++) a[i][sz] = 1;
     76 }
     77 
     78 ull quick_mod(ull a, ull b)
     79 {
     80     ull c = 1;
     81     while(b) {
     82         if(b&1) c = c * a;
     83         a = a * a;
     84         b >>= 1;
     85     }
     86     return c;
     87 }
     88 ///pow(a,1)+pow(a,2)+...+pow(a,x);
     89 ull sum_pow(ull a, ull x)
     90 {
     91     if(x == 1) return a;
     92     if(x&1) return quick_mod(a,x) + sum_pow(a, x-1);
     93     return sum_pow(a, x>>1) * (quick_mod(a, x>>1)+1);
     94 }
     95 void mul(ull a[N][N], ull b[N][N])
     96 {
     97     ull c[N][N] = {0};
     98     for(int k=0; k<=sz; k++)
     99         for(int i=0; i<=sz; i++)
    100             for(int j=0; j<=sz; j++)
    101             c[i][j] += a[i][k]*b[k][j];
    102     memcpy(a, c, sizeof c);
    103 }
    104 void mat_pow(ull a[N][N], ull b)
    105 {
    106     ull c[N][N] = {0};
    107     for(int i=0; i<=sz; i++)
    108         c[i][i] = 1;
    109     while(b){
    110         if(b&1) mul(c, a);
    111         mul(a, a);
    112         b >>= 1;
    113     }
    114     memcpy(a, c, sizeof c);
    115 }
    116 
    117 char s[12];
    118 int main()
    119 {
    120     int i,j,k;
    121     ull n, m;
    122     while(scanf("%I64u %I64u",&n,&m) == 2)
    123     {
    124         init();
    125         for(i=1; i<=n; i++) scanf("%s",s), Insert(s);
    126         build();
    127         ull a[N][N] = {0};
    128         get_matric(a);
    129         mat_pow(a, m);
    130         ull ans = 0;
    131         for(i=0; i<=sz; i++) ans += a[0][i];
    132         printf("%I64u
    ",sum_pow(26,m)-ans+1);
    133     }
    134     return 0;
    135 }
    View Code

    6.poj 1625  Censored!

      给你一个不超过50的字典,然后还有不超过10个长度不超过50的非法串,问有多少个由字典组成的长度为m的串?没有取模。

    我真想喷这题,没有任何思维上的新点,就是恶心+恶心。

    1).卡内存,明显可以按poj 2778的套路,构造500*500矩阵搞就行了,但是,卡内存啊。注意到m不大,那就dp吧,dp[i][j]表示串长为i时在自动机j节点上的合法串的数量,状态转移也很简单:遍历字母表,如果j节点接收某个字符后转移到k节点,那么dp[i][k] += dp[i-1][j],初始dp[0][0] = 1。

    2).ASCALL可高达255,我用java写的更坑爹,由于java中char是2个字节,有时会把输入的两个字符当成一个字符输入。。好吧,又改成了byte型的才行。

    3).那么到底输入的p个串是由字母表构成的吗?我怎么测出来非法串还有其他字符啊,按照大家的做法,把不在字典中的字符哈希为0就稀里糊涂过了。

    真的是够了,出题人的良心呢?

      1 import java.math.BigInteger;
      2 import java.util.Arrays;
      3 import java.util.LinkedList;
      4 import java.util.Queue;
      5 import java.util.Scanner;
      6 
      7 public class Main{
      8     static int alp;
      9     static final int maxn = 505;
     10     static int ch[][] = new int[maxn][55];
     11     static int ed[] = new int[maxn];
     12     static int fail[] = new int[maxn];
     13     static int Hash[] = new int[66666];
     14     static BigInteger dp[][] = new BigInteger[55][maxn];
     15     static int root, sz;
     16     static Queue<Integer> q = new LinkedList<Integer>();
     17     
     18     static int newnode() {
     19         Arrays.fill(ch[sz], -1);
     20         ed[sz++] = 0;
     21         return sz-1;
     22     }
     23     
     24     static void init()
     25     {
     26         sz = 0;
     27         root = newnode();
     28     }
     29     
     30     static void Insert(byte str[]){
     31         int now = root;
     32         for(int i=0; i<str.length; i++) {
     33             int t = str[i];
     34             t += 128;
     35             if(ch[now][Hash[t]] == -1)
     36                 ch[now][Hash[t]] = newnode();
     37             now = ch[now][Hash[t]];
     38         }
     39         ed[now] = 1;
     40     }
     41     
     42     static void build()
     43     {
     44         q.clear();
     45         fail[root] = root;
     46         for(int i=0; i<alp; i++){
     47             if(ch[root][i] == -1) ch[root][i] = root;
     48             else {
     49                 fail[ch[root][i]] = root; q.add(ch[root][i]);
     50             }
     51         }
     52         while(!q.isEmpty()) {
     53             int now = q.remove();
     54             for(int i=0; i<alp; i++)
     55             {
     56                 if(ch[now][i] == -1) ch[now][i] = ch[fail[now]][i];
     57                 else {
     58                     fail[ch[now][i]] = ch[fail[now]][i];
     59                     ed[ch[now][i]] |= ed[fail[ch[now][i]]];
     60                     q.add(ch[now][i]);
     61                 }
     62             }
     63         }
     64     }
     65     static void memset(BigInteger a[][], BigInteger x)
     66     {
     67         for(int i=0; i<a.length; i++) 
     68             for(int j=0; j<a[i].length; j++)
     69                 a[i][j] = x;
     70     }
     71     public static void main(String[] args) {
     72         @SuppressWarnings("resource")
     73         Scanner in = new Scanner(System.in);
     74         int n, m, p;
     75         while(in.hasNext())
     76         {
     77             n = in.nextInt(); m = in.nextInt(); p = in.nextInt(); 
     78             Arrays.fill(Hash, 0);
     79             byte s[] = in.next().getBytes();
     80             for(int i=0; i<n; i++) {
     81                 int t = s[i];
     82                 t += 128;
     83                 Hash[t] = i;
     84             }
     85             init();
     86             alp = n;
     87             for(int i=1; i<=p; i++)
     88             {
     89                 byte str[] = in.next().getBytes();
     90                 Insert(str);
     91             }
     92             build();
     93             memset(dp, BigInteger.ZERO);
     94             dp[0][0] = BigInteger.ONE;
     95             for(int i=1; i<=m; i++)
     96                 for(int j=0; j<sz; j++)
     97                 {
     98                     if(ed[j] > 0) continue;
     99                     for(int k=0; k<n; k++)
    100                     {
    101                         int tmp = ch[j][k];
    102                         if(ed[tmp] > 0) continue;
    103                         dp[i][tmp] = dp[i][tmp].add(dp[i-1][j]);
    104                     }
    105                 }
    106             BigInteger ans = BigInteger.ZERO;
    107             for(int i=0; i<sz; i++) ans = ans.add(dp[m][i]);
    108             System.out.println(ans);
    109         }
    110     }
    111 }
    View Code

    7.hdu 2825  Wireless Password

      给m个单词,问长为n的至少包含k个单词的串有多少个?(0<=m<=10, 1<=n<=25)

    dp的状态和当前串长、Trie节点、已含单词集有关,那么dp[i][j][k]就表示当前串长为i,在Trie的j节点,状态为k(0<=k<2^10)。初始dp[0][0][0] = 1。

      有两种转移方式:要么枚举dp[i][j][k]的前一个状态加到dp[i][j][k],要么枚举dp[i][j][k]的后一个状态,将dp[i][j][k]的值加到后一个状态。

    复杂度均为O(25*100*2048*26),但是时限比较紧,我们选择后者方式,如果dp[i][j][k]=0,直接continue,它对后续状态无贡献。再者,数据数较多,不能每次用memset清空dp数组。这样优化后大概就能A掉了。

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<cmath>
      5 #include<algorithm>
      6 #include<queue>
      7 using namespace std;
      8 typedef unsigned long long ull;
      9 const int N = 50;
     10 const int mod = 20090717;
     11 const int alp = 26;
     12 const int maxn = 555;
     13 int ch[maxn][alp], ed[maxn], fail[maxn];
     14 int root, sz;
     15 
     16 int newnode()
     17 {
     18     memset(ch[sz], -1, sizeof(ch[sz]));
     19     ed[sz++] = 0;
     20     return sz-1;
     21 }
     22 void init()
     23 {
     24     sz = 0;
     25     root = newnode();
     26 }
     27 
     28 void Insert(char str[], int id)
     29 {
     30     int now = root;
     31     for(int i=0; str[i]; i++)
     32     {
     33         int &tmp = ch[now][str[i]-'a'];
     34         if(tmp == -1) tmp = newnode();
     35         now = tmp;
     36     }
     37     ed[now] |= 1<<id-1;
     38 }
     39 
     40 void build()
     41 {
     42     queue<int> q;
     43     fail[root] = root;
     44     for(int i=0; i<alp; i++)
     45     {
     46         int &tmp = ch[root][i];
     47         if(tmp == -1) tmp = root;
     48         else fail[tmp] = root, q.push(tmp);
     49     }
     50     while(!q.empty())
     51     {
     52         int now = q.front();
     53         q.pop();
     54         for(int i=0; i<alp; i++)
     55         {
     56             int &tmp = ch[now][i];
     57             if(tmp == -1) tmp = ch[fail[now]][i];
     58             else fail[tmp] = ch[fail[now]][i],
     59                                  ed[tmp] |= ed[fail[tmp]],
     60                                             q.push(tmp);
     61         }
     62     }
     63 }
     64 char str[25];
     65 int dp[26][110][1<<10];
     66 int num[1<<10];
     67 
     68 int solve(int n, int m, int k)
     69 {
     70     for(int i=0; i<=n; i++)
     71         for(int j=0; j<sz; j++)
     72             for(int sta=0; sta<(1<<m); sta++)
     73                 dp[i][j][sta] = 0;
     74     dp[0][0][0] = 1;
     75     /*****************************************
     76     for(int len=1; len<=n; len++)
     77         for(int pre=0; pre<sz; pre++)
     78             for(int sta=0; sta<(1<<m); sta++)
     79     {
     80         for(int a=0; a<alp; a++)
     81         {
     82             int tmp = ch[pre][a];
     83             if(ed[tmp] && !(sta&(1<<ed[tmp]-1))) continue;
     84             int &p = dp[len][tmp][sta];
     85             if(ed[tmp])
     86                 p = (p + dp[len-1][pre][sta]+dp[len-1][pre][sta^(1<<ed[tmp]-1)]) % mod;
     87             else p = (p + dp[len-1][pre][sta])%mod;
     88             if(p > 1000000000) p %= mod;
     89         }
     90     }
     91     *****************************************************/
     92     for(int len=0; len<n; len++)
     93         for(int pre=0; pre<sz; pre++)
     94             for(int sta=0; sta<(1<<m); sta++)
     95             {
     96                 if(dp[len][pre][sta] == 0) continue;
     97                 for(int a=0; a<alp; a++)
     98                 {
     99                     int tmp = ch[pre][a];
    100                     int &pp = dp[len+1][tmp][sta|ed[tmp]];
    101                     pp += dp[len][pre][sta];
    102                     if(pp > 1000000000) pp %= mod;
    103                 }
    104             }
    105     int ans = 0;
    106     for(int now=0; now<sz; now++)
    107         for(int sta=0; sta<(1<<m); sta++)
    108             if(num[sta] >= k)
    109             {
    110                 ans += dp[n][now][sta];
    111                 if(ans > 1000000000) ans %= mod;
    112             }
    113     return ans%mod;
    114 }
    115 int main()
    116 {
    117     int i,j,k,m,n;
    118     memset(num, 0, sizeof(num));
    119     num[0] = 0, num[1] = 1;
    120     for(int i=2; i<(1<<10); i++)
    121         num[i] = num[i>>1] + (i&1);
    122     while(scanf("%d %d %d",&n,&m,&k) == 3 && n+m+k)
    123     {
    124         init();
    125         for(i=1; i<=m; i++)
    126             scanf("%s",str), Insert(str, i);
    127         build();
    128         printf("%d
    ",solve(n,m,k));
    129     }
    130     return 0;
    131 }
    View Code

    8.hdu 2296  Ring

      给m个单词和长为L的串,问至少需要改变几个字符使得L串不包含任意一个单词。单词50个*20,L<=1000,字母均由'A''C''G''T'组成。

      令dp[i][j]表示子串s[0,i-1]止于状态j不包含单词所需要改变字符数,初始dp数组inf, dp[0][0] = 0。那么dp[i][j] = min{dp[i-1][k]+(s[i-1] != c) | k由字符c转移到j}。同样,也可以由dp[i][j]来更新dp[i+1][k],并且加入dp[i][j] != inf优化。

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<cmath>
      5 #include<algorithm>
      6 #include<queue>
      7 using namespace std;
      8 typedef unsigned long long ull;
      9 const int inf = 0x3f3f3f3f;
     10 const int N = 1111;
     11 const int alp = 4;
     12 const int maxn = 555;
     13 int Hash['Z'];
     14 char ALP[4] = {'A','C','G','T'};
     15 int ch[maxn][alp], ed[maxn], fail[maxn];
     16 int root, sz;
     17 
     18 int newnode()
     19 {
     20     memset(ch[sz], -1, sizeof(ch[sz]));
     21     ed[sz++] = 0;
     22     return sz-1;
     23 }
     24 void init()
     25 {
     26     sz = 0;
     27     root = newnode();
     28 }
     29 
     30 void Insert(char str[])
     31 {
     32     int now = root;
     33     for(int i=0; str[i]; i++)
     34     {
     35         int &tmp = ch[now][Hash[str[i]]];
     36         if(tmp == -1) tmp = newnode();
     37         now = tmp;
     38     }
     39     ed[now] = 1;
     40 }
     41 
     42 void build()
     43 {
     44     queue<int> q;
     45     fail[root] = root;
     46     for(int i=0; i<alp; i++)
     47     {
     48         int &tmp = ch[root][i];
     49         if(tmp == -1) tmp = root;
     50         else fail[tmp] = root, q.push(tmp);
     51     }
     52     while(!q.empty())
     53     {
     54         int now = q.front();
     55         q.pop();
     56         for(int i=0; i<alp; i++)
     57         {
     58             int &tmp = ch[now][i];
     59             if(tmp == -1) tmp = ch[fail[now]][i];
     60             else fail[tmp] = ch[fail[now]][i],
     61                 ed[tmp] |= ed[fail[tmp]],
     62                 q.push(tmp);
     63         }
     64     }
     65 }
     66 char str[1100];
     67 int dp[N][maxn];
     68 
     69 void solve()
     70 {
     71     int len = strlen(str);
     72     memset(dp, 0x3f, sizeof(dp));
     73     dp[0][0] = 0;
     74     int now = root;
     75     for(int len=0; str[len]; len++)
     76         for(int pre=0; pre<sz; pre++)
     77     {
     78         if(ed[pre]) continue;
     79         if(dp[len][pre] == inf) continue;
     80         for(int a=0; a<alp; a++)
     81         {
     82             int tmp = ch[pre][a];
     83             if(ed[tmp]) continue;
     84             dp[len+1][tmp] = min(dp[len+1][tmp], dp[len][pre]+(str[len] != ALP[a]));
     85         }
     86     }
     87     int ans = inf;
     88     for(int i=0; i<sz; i++)
     89         if(!ed[i]) ans = min(ans, dp[len][i]);
     90     if(ans == inf) puts("-1");
     91     else printf("%d
    ",ans);
     92 }
     93 int main()
     94 {
     95     int i,j,k,m,n,t;
     96     int cas = 0;
     97     Hash['A'] = 0, Hash['C'] = 1, Hash['G'] = 2, Hash['T'] = 3;
     98     while(scanf("%d",&n) == 1 && n)
     99     {
    100         init();
    101         for(i=1; i<=n; i++)
    102             scanf("%s",str), Insert(str);
    103         build();
    104         scanf("%s",str);
    105         printf("Case %d: ",++cas);
    106         solve();
    107     }
    108     return 0;
    109 }
    View Code

    9.zoj 3228  Searching the String

      给一个长串len<=10^5,给N个0/1属性的长度不超过6的字符串,N<=10^5。

      输出每个串在长串中匹配的个数,属性为0/1分别表示可/不可重叠。

      对所有小串建立AC自动机,每个节点保留0/1属性串匹配的个数,再记录上一次1串匹配到此处的时间,若匹配到此处找到一串,那么只有时间差大于等于串的长度才能+1并更新此时间。0串完全是模板,直接++即可。

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<cmath>
      5 #include<algorithm>
      6 #include<queue>
      7 using namespace std;
      8 typedef unsigned long long ull;
      9 const int inf = 0x3f3f3f3f;
     10 const int N = 100010;
     11 const int alp = 26;
     12 const int maxn = 666666;
     13 int ch[maxn][alp], ed[maxn][2], fail[maxn];
     14 int Hash[N];
     15 int root, sz;
     16 int tim[maxn], num[maxn][2], len[maxn];
     17 int newnode()
     18 {
     19     memset(ch[sz], -1, sizeof(ch[sz]));
     20     tim[sz] = 0;
     21     ed[sz][0] = ed[sz][1] = 0;
     22     num[sz][0] = num[sz][1] = 0;
     23     return sz++;
     24 }
     25 void init()
     26 {
     27     sz = 0;
     28     root = newnode();
     29 }
     30 
     31 void Insert(char str[], int tag, int id)
     32 {
     33     int now = root, i;
     34     for(i=0; str[i]; i++)
     35     {
     36         int &tmp = ch[now][str[i]-'a'];
     37         if(tmp == -1) tmp = newnode();
     38         now = tmp;
     39     }
     40     Hash[id] = now;
     41     ed[now][tag] = 1;
     42     len[now] = i;
     43 }
     44 
     45 void build()
     46 {
     47     queue<int> q;
     48     fail[root] = root;
     49     for(int i=0; i<alp; i++)
     50     {
     51         int &tmp = ch[root][i];
     52         if(tmp == -1) tmp = root;
     53         else fail[tmp] = root, q.push(tmp);
     54     }
     55     while(!q.empty())
     56     {
     57         int now = q.front();
     58         q.pop();
     59         for(int i=0; i<alp; i++)
     60         {
     61             int &tmp = ch[now][i];
     62             if(tmp == -1) tmp = ch[fail[now]][i];
     63             else fail[tmp] = ch[fail[now]][i],
     64                 q.push(tmp);
     65         }
     66     }
     67 }
     68 
     69 char str[maxn], s[11];
     70 int tag[N];
     71 void solve(int n)
     72 {
     73     int now = root;
     74     for(int i=0; str[i]; i++)
     75     {
     76         int tmp = now = ch[now][str[i]-'a'];
     77         while(tmp != root)
     78         {
     79             if(ed[tmp][0]) {
     80                 num[tmp][0]++;
     81             }
     82             if(ed[tmp][1] && i+1-tim[tmp] >= len[tmp]) {
     83                 num[tmp][1]++;
     84                 tim[tmp] = i+1;
     85             }
     86             tmp = fail[tmp];
     87         }
     88     }
     89     for(int i=1; i<=n; i++)
     90         printf("%d
    ",num[Hash[i]][tag[i]]);
     91     puts("");
     92 }
     93 
     94 int main()
     95 {
     96     int i,j,k,m,n,t,cas=0;
     97     while(scanf("%s",str) == 1)
     98     {
     99         scanf("%d",&n);
    100         init();
    101         for(i=1; i<=n; i++)
    102         {
    103             scanf("%d %s",&m, s);
    104             tag[i] = m;
    105             Insert(s, m, i);
    106         }
    107         build();
    108         printf("Case %d
    ",++cas);
    109         solve(n);
    110     }
    111     return 0;
    112 }
    View Code

    10.hdu 3341 RLost's revenge

      给N个Len<=10的单词,再给一个长不超过40的串,将此串字母位置重置,使得包含最多的单词,N<=50。所有单词仅由字母'A''C''G''T'组成。

      我们不用考虑输入长串的形态,只记录各个字母的个数就行了。dp很好想,令dp[i][j]表示在自动机i节点,字母的使用情况为j时包含的最多的单词数量。

    难点就在于:如何表示'A''C''G''T'的使用情况?

      假设有num[0]个'A',num[1]个'C',num[2]个'G',num[3]个'T',那么使用了a个'A',c个'C',g个'G',t个'T'可表示为a*(num[1]+1)*(num[2]+1)*(num[3]+1) + c*(num[2]+1)*num[3]+1) + g*(num[3]+1) + t。字母数均分时最大且<11*11*11*11。

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<cmath>
      5 #include<algorithm>
      6 #include<queue>
      7 using namespace std;
      8 typedef unsigned long long ull;
      9 const int inf = 0x3f3f3f3f;
     10 const int N = 11*11*11*11+5;
     11 const int alp = 4;
     12 const int maxn = 555;
     13 int ch[maxn][alp], ed[maxn], fail[maxn];
     14 int Hash['Z'];
     15 int root, sz;
     16 int newnode()
     17 {
     18     memset(ch[sz], -1, sizeof(ch[sz]));
     19     ed[sz] = 0;
     20     return sz++;
     21 }
     22 void init()
     23 {
     24     sz = 0;
     25     root = newnode();
     26 }
     27 
     28 void Insert(char str[])
     29 {
     30     int now = root, i;
     31     for(i=0; str[i]; i++)
     32     {
     33         int &tmp = ch[now][Hash[str[i]]];
     34         if(tmp == -1) tmp = newnode();
     35         now = tmp;
     36     }
     37     ed[now] ++;
     38 }
     39 
     40 void build()
     41 {
     42     queue<int> q;
     43     fail[root] = root;
     44     for(int i=0; i<alp; i++)
     45     {
     46         int &tmp = ch[root][i];
     47         if(tmp == -1) tmp = root;
     48         else fail[tmp] = root, q.push(tmp);
     49     }
     50     while(!q.empty())
     51     {
     52         int now = q.front();
     53         q.pop();
     54         ed[now] += ed[fail[now]];
     55         for(int i=0; i<alp; i++)
     56         {
     57             int &tmp = ch[now][i];
     58             if(tmp == -1) tmp = ch[fail[now]][i];
     59             else fail[tmp] = ch[fail[now]][i],
     60                  q.push(tmp);
     61         }
     62     }
     63 }
     64 int dp[maxn][N];
     65 char str[44];
     66 int num[4];
     67 void Max(int &a, int b) {a = max(a, b);}
     68 int main()
     69 {
     70     int i,j,k,m,n,t,cas=0;
     71     Hash['A'] = 0, Hash['C']= 1, Hash['G'] = 2, Hash['T'] = 3;
     72     while(scanf("%d",&n) == 1 && n)
     73     {
     74         init();
     75         memset(num, 0, sizeof(num));
     76         for(int i=1; i<=n; i++)
     77             scanf("%s",str), Insert(str);
     78         build();
     79         scanf("%s",str);
     80         for(i=0; str[i]; i++) num[Hash[str[i]]]++;
     81         memset(dp, 0, sizeof(dp));
     82         int mod3 = num[3]+1;
     83         int mod2 = mod3 * (num[2]+1);
     84         int mod1 = mod2 * (num[1]+1);
     85         int limit = num[0]*mod1+num[1]*mod2+num[2]*mod3+num[3];
     86         memset(dp, -1, sizeof(dp));
     87         dp[0][0] = 0;
     88         for(int a=0; a<=num[0]; a++)
     89             for(int b=0; b<=num[1]; b++)
     90                 for(int c=0; c<=num[2]; c++)
     91                     for(int d=0; d<=num[3]; d++)
     92                     {
     93                         int tp = a*mod1 + b*mod2 + c*mod3 + d;
     94                         for(int i=0; i<sz; i++)
     95                         {
     96                             if(dp[i][tp] == -1) continue;
     97                             if(a < num[0]) Max(dp[ch[i][0]][tp+mod1], dp[i][tp]+ed[ch[i][0]]);
     98                             if(b < num[1]) Max(dp[ch[i][1]][tp+mod2], dp[i][tp]+ed[ch[i][1]]);
     99                             if(c < num[2]) Max(dp[ch[i][2]][tp+mod3], dp[i][tp]+ed[ch[i][2]]);
    100                             if(d < num[3]) Max(dp[ch[i][3]][tp+1], dp[i][tp]+ed[ch[i][3]]);
    101                         }
    102                     }
    103         int ans = 0;
    104         for(int i=0; i<sz; i++)
    105             for(int j=0; j<=limit; j++)
    106                 Max(ans, dp[i][j]);
    107         printf("Case %d: %d
    ",++cas, ans);
    108 
    109     }
    110     return 0;
    111 }
    View Code

    11. hdu 3247  Resource Archiver

      给n个资源串和m病毒串,现要把资源串串起来,两串可以重叠,且不含病毒串,求串起来的最短长度。2 <= n <= 10, 1 <= m <= 1000,资源串长不超过10^5,病毒串总长不超过5w。所有串仅由'0''1'组成。

      很直接的DP方式就是用dp[i][j]表示在Trie树i节点时,匹配情况是j的最短长度。但是6w*(1<<10)MLE。。。

    翻了翻网上的题解,大都是说让j表示在第j个单词的末尾。加单词间的最短路。仔细想一想,不对啊,有的资源串的后缀包含了另一个资源,那么另一个资源串不就有多个结尾了吗?

      还有一种解释是说把带有标记的节点拿出来,最短路+dp,尼玛,最坏情况下每个节点都有标记啊。

      我看了看他们的代码,发现都一样,那就一个结论了:所有资源串都不是另一个资源串的前缀或后缀。好吧,那就直接用单词末尾的节点来dp吧,先BFS处理任意两个单词末节点的最短路,它的意义是第二个串长与第一个串后缀重合部分长的差,BFS不经过病毒节点就行了。

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<cmath>
      5 #include<algorithm>
      6 #include<queue>
      7 using namespace std;
      8 typedef unsigned long long ull;
      9 const int inf = 0x3f3f3f3f;
     10 const int N = 55555;
     11 const int alp = 2;
     12 const int maxn = 60066;
     13 int ch[maxn][alp], ed[2][maxn], fail[maxn];
     14 int ID[11]; ///第i个串末在自动机中的编号
     15 int root, sz;
     16 int newnode()
     17 {
     18     memset(ch[sz], -1, sizeof(ch[sz]));
     19     ed[0][sz] = ed[1][sz] = 0;
     20     return sz++;
     21 }
     22 void init()
     23 {
     24     sz = 0;
     25     root = newnode();
     26 }
     27 
     28 void Insert(char str[], int id, int tag)
     29 {
     30     int now = root, i;
     31     for(i=0; str[i]; i++)
     32     {
     33         int &tmp = ch[now][str[i]-'0'];
     34         if(tmp == -1) tmp = newnode();
     35         now = tmp;
     36     }
     37     ed[id][now] |= (1<<tag-1);
     38     if(!id) ID[tag] = now;
     39 }
     40 
     41 void build()
     42 {
     43     queue<int> q;
     44     fail[root] = root;
     45     for(int i=0; i<alp; i++)
     46     {
     47         int &tmp = ch[root][i];
     48         if(tmp == -1) tmp = root;
     49         else fail[tmp] = root, q.push(tmp);
     50     }
     51     while(!q.empty())
     52     {
     53         int now = q.front();
     54         q.pop();
     55         ed[0][now] |= ed[0][fail[now]];
     56         ed[1][now] |= ed[1][fail[now]];
     57         for(int i=0; i<alp; i++)
     58         {
     59             int &tmp = ch[now][i];
     60             if(tmp == -1) tmp = ch[fail[now]][i];
     61             else fail[tmp] = ch[fail[now]][i],
     62                  q.push(tmp);
     63         }
     64     }
     65 }
     66 int dis[maxn];
     67 bool vis[maxn];
     68 int w[11][11];
     69 void get_dis(int s, int n)
     70 {
     71     memset(dis, 0x3f, sizeof(dis));
     72     memset(vis, 0, sizeof(vis));
     73     dis[ID[s]] = 0;
     74     vis[ID[s]] = 1;
     75     queue<int>q;
     76     q.push(ID[s]);
     77     while(!q.empty()) {
     78         int u = q.front(); q.pop();
     79         for(int i=0; i<alp; i++)
     80         {
     81             int tmp = ch[u][i];
     82             if(ed[1][tmp]) continue;
     83             if(vis[tmp]) continue;
     84             vis[tmp] = 1;
     85             dis[tmp] = dis[u] + 1;
     86             q.push(tmp);
     87         }
     88     }
     89     for(int i=1; i<=n; i++)
     90         w[s][i] = dis[ID[i]];
     91 }
     92 char str[N];
     93 int len[11];
     94 int dp[1<<10][11];
     95 int main()
     96 {
     97     int i,j,k,m,n;
     98     int p[11];
     99     while(scanf("%d %d",&n,&m) == 2 && n+m)
    100     {
    101         init();
    102         int sum = 0;
    103         for(i=1; i<=n; i++)
    104             scanf("%s", str), len[i] =  strlen(str), Insert(str, 0, i);
    105         for(i=1; i<=m; i++)
    106             scanf("%s", str), Insert(str, 1, i);
    107         build();
    108         ID[0] = root;
    109         memset(w, 0x3f, sizeof(w));
    110         for(i=0; i<=n; i++) get_dis(i,n);
    111         int ans = inf;
    112         /***********************************
    113          *枚举顺序,简单粗暴
    114         for(i=1; i<=n; i++) p[i] = i;
    115         do {
    116             int ret = 0;
    117             for(j=1; j<n; j++)
    118                 ret += w[p[j]][p[j+1]];
    119             ans = min(ans, ret+len[p[1]]);
    120         } while(next_permutation(p+1, p+1+n));
    121         ************************************/
    122         memset(dp, 0x3f, sizeof(dp));
    123         dp[0][0] = 0;
    124         for(i=0; i<(1<<n); i++)
    125             for(j=0; j<=n; j++)
    126         {
    127             if(dp[i][j] == inf) continue;
    128             for(k=1; k<=n; k++)
    129             {
    130                 if(w[j][k] == inf) continue;
    131                 dp[i|(1<<k-1)][k] = min(dp[i|(1<<k-1)][k], dp[i][j] + w[j][k]);
    132             }
    133         }
    134         for(i=1; i<=n; i++) ans = min(ans, dp[(1<<n)-1][i]);
    135         printf("%d
    ",ans);
    136     }
    137     return 0;
    138 }
    View Code

    12. zoj 3494  Detect the Viru

      给n个01病毒串,然后问区间[L,R]内有多少个数的BCD encoding不包含病毒串。一个数的BCD encoding是指每一位数的4bit位连起来的串。

      n<=100,病毒串长小于20,L<=R<10^200。

      很好想的数位DP+AC自动机dp[i][j]表示在还有i位且当前在Trie数的j节点的合法数字的个数。

      注意,如果用字符串读入L和R,要特判L。

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<cmath>
      5 #include<algorithm>
      6 #include<queue>
      7 using namespace std;
      8 typedef unsigned long long ull;
      9 const int inf = 0x3f3f3f3f;
     10 const int mod = 1000000009;
     11 const int N = 222;
     12 const int alp = 10;
     13 const int maxn = 2222;
     14 int ch[maxn][alp], ed[maxn], fail[maxn];
     15 int root, sz;
     16 int newnode()
     17 {
     18     memset(ch[sz], -1, sizeof(ch[sz]));
     19     ed[sz] = 0;
     20     return sz++;
     21 }
     22 void init()
     23 {
     24     sz = 0;
     25     root = newnode();
     26 }
     27 
     28 void Insert(char str[])
     29 {
     30     int now = root, i;
     31     for(i=0; str[i]; i++)
     32     {
     33         int &tmp = ch[now][str[i]-'0'];
     34         if(tmp == -1) tmp = newnode();
     35         now = tmp;
     36     }
     37     ed[now] = 1;
     38 }
     39 
     40 void build()
     41 {
     42     queue<int> q;
     43     fail[root] = root;
     44     for(int i=0; i<alp; i++)
     45     {
     46         int &tmp = ch[root][i];
     47         if(tmp == -1) tmp = root;
     48         else fail[tmp] = root, q.push(tmp);
     49     }
     50     while(!q.empty())
     51     {
     52         int now = q.front();
     53         q.pop();
     54         ed[now] |= ed[fail[now]];
     55         for(int i=0; i<alp; i++)
     56         {
     57             int &tmp = ch[now][i];
     58             if(tmp == -1) tmp = ch[fail[now]][i];
     59             else fail[tmp] = ch[fail[now]][i],
     60                  q.push(tmp);
     61         }
     62     }
     63 }
     64 
     65 int sel[10][4] = {0,0,0,0, 0,0,0,1, 0,0,1,0, 0,0,1,1, 0,1,0,0,
     66                   0,1,0,1, 0,1,1,0, 0,1,1,1, 1,0,0,0, 1,0,0,1};
     67 int query(char str[])
     68 {
     69     int len = strlen(str+1);
     70     int now = root;
     71     for(int i=1; i<=len; i++)
     72     {
     73         int a = str[i] - '0';
     74         for(int j=0; j<4; j++)
     75         {
     76             int tmp = now = ch[now][sel[a][j]];
     77             while(tmp) {
     78                 if(ed[tmp]) return 0;
     79                 tmp = fail[tmp];
     80             }
     81         }
     82     }
     83     return 1;
     84 }
     85 char s[N];
     86 int dp[N][maxn][2];
     87 int DFS(int pos, int id, bool limit, bool pre)
     88 {
     89     if(!pos) return pre;
     90     if(!limit && dp[pos][id][pre] != -1) return dp[pos][id][pre];
     91     int ret = 0;
     92     int end = limit ? s[pos]-'0' : 9;
     93     for(int i=0; i<=end; i++)
     94     {
     95         bool ok = true;
     96         int tmp = id;
     97         if(pre || i)
     98         for(int j=0; j<4; j++)
     99         {
    100             tmp = ch[tmp][sel[i][j]];
    101             if(ed[tmp]) ok = false;
    102         }
    103         if(ok) ret =(ret+DFS(pos-1, tmp, limit&&i==end, pre || i))%mod;
    104     }
    105     if(!limit) dp[pos][id][pre] = ret;
    106     return ret;
    107 }
    108 int main()
    109 {
    110     int i,j,k,m,n,t;
    111     scanf("%d",&t);
    112     while(t--)
    113     {
    114         init();
    115         scanf("%d",&n);
    116         while(n--) scanf("%s",s+1), Insert(s+1);
    117         build();
    118         scanf("%s",s+1);
    119         m = strlen(s+1);
    120         int ans = -query(s);
    121         reverse(s+1, s+1+m);
    122         for(i=0; i<=m; i++) for(j=0; j<sz; j++) dp[i][j][0]=dp[i][j][1]=-1;
    123         ans += DFS(m, 0, 1, 0);
    124         scanf("%s",s+1);
    125         m = strlen(s+1);
    126         reverse(s+1, s+1+m);
    127         for(i=0; i<=m; i++) for(j=0; j<sz; j++) dp[i][j][0]=dp[i][j][1]=-1;
    128         ans = (DFS(m, 0, 1, 0) - ans)%mod;
    129         printf("%d
    ",(ans+mod)%mod);
    130     }
    131     return 0;
    132 }
    View Code

    入门题暂时做到这里了( ^_^ )/~~。

  • 相关阅读:
    单元测试之mock使用
    Linux命令 diff 命令(比较文件的差异)
    @ComponentScan自动扫描组件介绍
    AES 加解密 & RSA加解密
    vxetable结合ElementUI单元格样式
    uniapp打开新的网页页签
    input等通过样式禁用
    elementui 时间选择限制当天之前的时间不可选择
    符号位扩展
    函数指针数组的运用
  • 原文地址:https://www.cnblogs.com/WCB-ACM/p/4903199.html
Copyright © 2020-2023  润新知