• AC自动机专题


    AC自动机简介:KMP是用于解决单模式串匹配问题, AC自动机用于解决多模式串匹配问题。

    精华:设这个节点上的字母为C,沿着他父亲的失败指针走,直到走到一个节点,他的儿子中也有字母为C的节点。然后把当前节点的失败指针指向那个字目也为C的儿子。如果一直走到了root都没找到,那就把失败指针指向root。

    如果用KMP来解决多模式串匹配问题,则复杂度为O(n + k * m), 而AC自动机的负责度为O(n + m + z), z为模式串出现的次数。

    学习链接:

    http://hi.baidu.com/nialv7/item/ce1ce015d44a6ba7feded52d

    http://blog.csdn.net/niushuai666/article/details/7002823

    http://www.cnblogs.com/kuangbin/p/3164106.html

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2222

    思路:AC自动机的入门题,用的是bin牛的模板,统计End数组即可,统计过的需要清0.

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 #include <queue>
     6 #define FOR(i, a, b) for (int i = (a); i < (b); ++i)
     7 #define REP(i, a, b) for (int i = (a); i <= (b); ++i)
     8 using namespace std;
     9 
    10 const int MAX_N = (500000 + 500);
    11 struct Trie {
    12     int next[MAX_N][26], End[MAX_N], fail[MAX_N];
    13     int root, L;
    14     int NewNode()
    15     {
    16         FOR(i, 0, 26) next[L][i] = -1;
    17         End[L++] = 0;
    18         return L - 1;
    19     }
    20     void Init()
    21     {
    22         L = 0;
    23         root = NewNode();
    24     }
    25     void Insert(char *str)
    26     {
    27         int len = strlen(str), now = root;
    28         FOR(i, 0, len) {
    29             int id = str[i] - 'a';
    30             if (next[now][id] == -1) next[now][id] = NewNode();
    31             now = next[now][id];
    32         }
    33         ++End[now];
    34     }
    35     void Build()
    36     {
    37         queue<int > que;
    38         fail[root] = root;
    39         FOR(i, 0, 26) {
    40             if (next[root][i] == -1) next[root][i] = root;
    41             else {
    42                 fail[next[root][i]] = root;
    43                 que.push(next[root][i]);
    44             }
    45         }
    46         while (!que.empty()) {
    47             int now = que.front();
    48             que.pop();
    49             FOR(i, 0, 26) {
    50                 if (next[now][i] == -1) {
    51                     next[now][i] = next[fail[now]][i];
    52                 } else {
    53                     fail[next[now][i]] = next[fail[now]][i];
    54                     que.push(next[now][i]);
    55                 }
    56             }
    57         }
    58     }
    59     int Query(char *str)
    60     {
    61         int len = strlen(str), now = root, res = 0;
    62         FOR(i, 0, len) {
    63             int id = str[i] - 'a';
    64             now = next[now][id];
    65             int tmp = now;
    66             while (tmp != root) {
    67                 res += End[tmp];
    68                 End[tmp] = 0;
    69                 tmp = fail[tmp];
    70             }
    71         }
    72         return res;
    73     }
    74 } AC;
    75 
    76 int n;
    77 char str[1000000 + 100];
    78 
    79 int main()
    80 {
    81     int Cas;
    82     scanf("%d", &Cas);
    83     while (Cas--) {
    84         AC.Init();
    85         scanf("%d", &n);
    86         REP(i, 1, n) {
    87             scanf("%s", str);
    88             AC.Insert(str);
    89         }
    90         AC.Build();
    91         scanf("%s", str);
    92         printf("%d
    ", AC.Query(str));
    93     }
    94     return 0;
    95 }
    View Code

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2896

    思路:和上题差不多,只是用End数组来记录序号而已。

      1 #include <iostream>
      2 #include <cstdio>
      3 #include <cstring>
      4 #include <algorithm>
      5 #include <queue>
      6 #include <vector>
      7 #define FOR(i, a, b) for (int i = (a); i < (b); ++i)
      8 #define REP(i, a, b) for (int i = (a); i <= (b); ++i)
      9 using namespace std;
     10 
     11 const int MAX_N = (100000 + 1000);
     12 struct Trie {
     13 
     14     int next[MAX_N][128], End[MAX_N], fail[MAX_N];
     15     int root, L;
     16     int NewNode() {
     17         FOR(i, 0, 128) next[L][i] = -1;
     18         End[L++] = 0;
     19         return L - 1;
     20     }
     21     void Init() {
     22         L = 0;
     23         root = NewNode();
     24     }
     25 
     26     void Insert(char *str, int index) {
     27         int len = strlen(str), now = root;
     28         FOR(i, 0, len) {
     29             int id = str[i];
     30             if (next[now][id] == -1) next[now][id] = NewNode();
     31             now = next[now][id];
     32         }
     33         End[now] = index;
     34     }
     35     void Build() {
     36         queue<int > que;
     37         fail[root] = root;
     38         FOR(i, 0, 128) {
     39             if (next[root][i] == -1) next[root][i] = root;
     40             else {
     41                 fail[next[root][i]] = root;
     42                 que.push(next[root][i]);
     43             }
     44         }
     45         while (!que.empty()) {
     46             int now = que.front();
     47             que.pop();
     48             FOR(i, 0, 128) {
     49                 if (next[now][i] == -1) {
     50                     next[now][i] = next[fail[now]][i];
     51                 } else {
     52                     fail[next[now][i]] = next[fail[now]][i];
     53                     que.push(next[now][i]);
     54                 }
     55             }
     56         }
     57     }
     58     void Query(char *str, vector<int > &ans) {
     59         int len = strlen(str), now = root;
     60         FOR(i, 0, len) {
     61             now = next[now][str[i]];
     62             int tmp = now;
     63             while (tmp != root) {
     64                 if (End[tmp]) ans.push_back(End[tmp]);
     65                 tmp = fail[tmp];
     66             }
     67         }
     68     }
     69 
     70 } AC;
     71 
     72 int N, M, res;
     73 char str[10000 + 100];
     74 vector<int > ans[1000 + 100];
     75 
     76 int main()
     77 {
     78     AC.Init();
     79     scanf("%d", &N);
     80     REP(i, 1, N) {
     81         scanf("%s", str);
     82         AC.Insert(str, i);
     83     }
     84     AC.Build();
     85     scanf("%d", &M);
     86     FOR(i, 0, M) {
     87         scanf("%s", str);
     88         AC.Query(str, ans[i]);
     89     }
     90     res = 0;
     91     FOR(i, 0, M) {
     92         if ((int)ans[i].size()) {
     93             printf("web %d:", i + 1);
     94             sort(ans[i].begin(), ans[i].end());
     95             FOR(j, 0, (int)ans[i].size()) printf(" %d", ans[i][j]);
     96             puts("");
     97             ++res;
     98         }
     99     }
    100     printf("total: %d
    ", res);
    101     return 0;
    102 }
    View Code

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3065

    思路:用一个数组来记录模式串在主串中出现的次数。

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 #include <queue>
     6 #define FOR(i, a, b) for (int i = (a); i < (b); ++i)
     7 #define REP(i, a, b) for (int i = (a); i <= (b); ++i)
     8 using namespace std;
     9 
    10 const int MAX_N = (50000 + 500);
    11 
    12 int N, num[1000 + 100];
    13 char ss[1000 + 100][55];
    14 char str[2000000 + 200];
    15 
    16 struct Trie {
    17     int next[MAX_N][128], End[MAX_N], fail[MAX_N];
    18     int root, L;
    19     int NewNode() {
    20         FOR(i, 0, 128) next[L][i] = -1;
    21         End[L++] = -1;
    22         return L - 1;
    23     }
    24 
    25     void Init() {
    26         L = 0;
    27         root = NewNode();
    28     }
    29 
    30     void Insert(char *str, int index) {
    31         int len = strlen(str), now = root;
    32         FOR(i, 0, len) {
    33             if (next[now][str[i]] == -1) next[now][str[i]] = NewNode();
    34             now = next[now][str[i]];
    35         }
    36         End[now] = index;
    37     }
    38 
    39     void Build() {
    40         queue<int > que;
    41         fail[root] = root;
    42         FOR(i, 0, 128) {
    43             if (next[root][i] == -1) next[root][i] = root;
    44             else {
    45                 fail[next[root][i]] = root;
    46                 que.push(next[root][i]);
    47             }
    48         }
    49         while (!que.empty()) {
    50             int now = que.front();
    51             que.pop();
    52             FOR(i, 0, 128) {
    53                 if (next[now][i] == -1) next[now][i] = next[fail[now]][i];
    54                 else {
    55                     fail[next[now][i]] = next[fail[now]][i];
    56                     que.push(next[now][i]);
    57                 }
    58             }
    59         }
    60     }
    61 
    62     void Query(char *str) {
    63         memset(num, 0, sizeof(num));
    64         int len = strlen(str), now = root;
    65         FOR(i, 0, len) {
    66             now = next[now][str[i]];
    67             int tmp = now;
    68             while (tmp != root) {
    69                 if (End[tmp] != -1) ++num[End[tmp]];
    70                 tmp = fail[tmp];
    71             }
    72         }
    73         FOR(i, 0, N) {
    74             if (num[i]) printf("%s: %d
    ", ss[i], num[i]);
    75         }
    76     }
    77 
    78 } AC;
    79 
    80 
    81 int main()
    82 {
    83     while (~scanf("%d", &N)) {
    84         AC.Init();
    85         scanf("%d", &N);
    86         FOR(i, 0, N) {
    87             scanf("%s", ss[i]);
    88             AC.Insert(ss[i], i);
    89         }
    90         AC.Build();
    91         scanf("%s", str);
    92         AC.Query(str);
    93     }
    94     return 0;
    95 }
    View Code

     题目链接:http://poj.org/problem?id=2778

    思路:需要用到的知识:有向图中点A到点B走K步的路径数等于有向图原始矩阵的K次幂。然后对于已经建好的Trie图,我们就可以建图了,如果某个节点A不是终止节点并且这个节点的next节点B也不是终止节点,那么就连边(表示从A点走1步到节点B的方法有1种)。建好图之后就是矩阵的快速幂了,然后在统计节点0(根节点)到其余节点走N步的方法数的总和。

      1 #include <iostream>
      2 #include <cstdio>
      3 #include <cstring>
      4 #include <algorithm>
      5 #include <queue>
      6 #define REP(i, a, b) for (int i = (a); i < (b); ++i)
      7 #define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
      8 using namespace std;
      9 
     10 const int MAX_N = (100 + 10);
     11 const int MOD = (100000);
     12 int M, N;
     13 char str[22];
     14 
     15 struct Matrix {
     16     long long mat[MAX_N][MAX_N];
     17     int n;
     18     Matrix() {}
     19     Matrix(int _n)
     20     {
     21         n = _n;
     22         REP(i, 0, n)
     23         REP(j, 0, n) mat[i][j] = 0;
     24     }
     25     Matrix operator *(const Matrix &b) const
     26     {
     27         Matrix c = Matrix(n);
     28         REP(i, 0, n) {
     29             REP(j, 0, n) {
     30                 REP(k, 0, n) {
     31                     c.mat[i][j] += mat[i][k] * b.mat[k][j];
     32                     if (c.mat[i][j] >= MOD) c.mat[i][j] %= MOD;
     33                 }
     34             }
     35         }
     36         return c;
     37     }
     38 
     39 };
     40 
     41 Matrix Pow(Matrix mat, int n)
     42 {
     43     Matrix ONE = Matrix(mat.n);
     44     REP(i, 0, mat.n) ONE.mat[i][i] = 1;
     45     Matrix tmp = mat;
     46     while (n) {
     47         if (n & 1) ONE = ONE * tmp;
     48         n >>= 1;
     49         tmp = tmp * tmp;
     50     }
     51     return ONE;
     52 }
     53 
     54 struct Trie {
     55     int next[MAX_N][4], End[MAX_N], fail[MAX_N];
     56     int L, root;
     57     int NewNode()
     58     {
     59         REP(i, 0, 4) next[L][i] = -1;
     60         End[L++] = 0;
     61         return L - 1;
     62     }
     63 
     64     void Init()
     65     {
     66         L = 0;
     67         root = NewNode();
     68     }
     69 
     70     int getID(char ch)
     71     {
     72         if (ch == 'A') return 0;
     73         if (ch == 'C') return 1;
     74         if (ch == 'G') return 2;
     75         if (ch == 'T') return 3;
     76     }
     77 
     78     void Insert(char *str)
     79     {
     80         int len = strlen(str), now = root;
     81         REP(i, 0, len) {
     82             int id = getID(str[i]);
     83             if (next[now][id] == -1) next[now][id] = NewNode();
     84             now = next[now][id];
     85         }
     86         End[now] = 1;
     87     }
     88 
     89     void Build()
     90     {
     91         queue<int > que;
     92         fail[root] = root;
     93         REP(i ,0, 4) {
     94             if (next[root][i] == -1) next[root][i] = root;
     95             else {
     96                 fail[next[root][i]] = root;
     97                 que.push(next[root][i]);
     98             }
     99         }
    100         while (!que.empty()) {
    101             int now =  que.front();
    102             que.pop();
    103             if (End[fail[now]]) End[now] = 1;
    104             REP(i, 0, 4) {
    105                 if (next[now][i] == -1) next[now][i] = next[fail[now]][i];
    106                 else {
    107                     fail[next[now][i]] = next[fail[now]][i];
    108                     que.push(next[now][i]);
    109                 }
    110             }
    111         }
    112     }
    113 
    114     Matrix getMatrix()
    115     {
    116         Matrix res = Matrix(L);
    117         REP(i, 0, L)
    118         REP(j, 0, 4) if (!End[next[i][j]]) ++res.mat[i][next[i][j]];
    119         return res;
    120     }
    121 
    122 } AC;
    123 
    124 
    125 int main()
    126 {
    127     while (~scanf("%d %d", &M, &N)) {
    128         AC.Init();
    129         FOR(i, 1, M) scanf("%s", str), AC.Insert(str);
    130         AC.Build();
    131         Matrix tmp = AC.getMatrix();
    132         tmp = Pow(tmp, N);
    133         long long ans = 0;
    134         REP(i, 0, tmp.n) {
    135             ans += tmp.mat[0][i];
    136             if (ans >= MOD) ans %= MOD;
    137         }
    138         printf("%lld
    ", ans);
    139     }
    140     return 0;
    141 }

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=224

     思路:出现过给定单词的单词数 = 所有的单词数 -  没有出现过给定单词的单词数, 而所有的单词数 = 26^1 + 26^2 + ... + 26^L,没有出现过给定单词的单词数 = A + A^2 + ... + A^n,其中A是根据能走的字符之间的路径数建立的邻接矩阵。

    那么如何求出A + A^2 + A^3 + ... + A^L?

    可以这样做,依据等比矩阵的特点有:


    于是可以很快计算出A^1+ A ^2 + ... + A ^L 以及 26^1 + 26^2 + ... + 26 ^ L的值。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    #define REP(i, a, b) for (int i = (a); i < (b); ++i)
    #define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
    using namespace std;
    
    const int MAX_N = (33);
    int N, L;
    typedef unsigned long long ULL;
    
    struct Matrix {
    	ULL mat[MAX_N][MAX_N];
    	int n;
    	Matrix(){}
    	Matrix(int _n) : n(_n) {
    		REP(i, 0, n)
    			REP(j, 0, n) mat[i][j] = 0;
    	}
    };
    
    Matrix Mul(const Matrix &a, const Matrix &b)
    {
    	Matrix c = Matrix(a.n);
    	REP(i, 0, a.n) {
    		REP(j, 0, a.n)
    			REP(k, 0, a.n) c.mat[i][j] += a.mat[i][k] * b.mat[k][j];
    	}
    	return c;
    }
    
    Matrix Pow(Matrix p,int n)
    {
    	Matrix ONE = Matrix(p.n);
    	REP(i, 0, ONE.n) ONE.mat[i][i] = 1;
    	while (n) {
    		if (n & 1) ONE = Mul(ONE, p);
    		n >>= 1;
    		p = Mul(p, p);
    	}
    	return ONE;
    }
    
    
    struct Trie {
    	int next[MAX_N][MAX_N], end[MAX_N], fail[MAX_N];
    	int root, L;
    	void Init() {
    		L = 0;
    		root = NewNode();
    	}
    
    	int NewNode() {
    		REP(i, 0, 26) next[L][i] = -1;
    		end[L++] = 0;
    		return (L - 1);
    	}
    
    	void Insert(char *str) {
    		int len = strlen(str), now = root;
    		REP(i, 0, len) {
    			int index = str[i] - 'a';
    			if (next[now][index] == -1)  next[now][index] = NewNode();
    			now = next[now][index];
    		}
    		end[now] = 1;
    	}
    
    	void Build() {
    		queue<int > que;
    		fail[root] = root;
    		REP(i, 0, 26) {
    			if (next[root][i] == -1) next[root][i]  = root;
    			else {
    				fail[next[root][i]] = root;
    				que.push(next[root][i]);
    			}
    		}
    		while (!que.empty()) {
    			int now = que.front(); que.pop();
    			if (end[fail[now]]) end[now] = 1;
    			REP(i, 0, 26) {
    				if (next[now][i] == -1) next[now][i] = next[fail[now]][i];
    				else {
    					fail[next[now][i]] = next[fail[now]][i];
    					que.push(next[now][i]);
    				}
    			}
    		}
    	}
    	Matrix getMatrix() {
    		Matrix tmp = Matrix(L + 1);
    		REP(i, 0, L) {
    			REP(j, 0, 26) if (!end[next[i][j]]) ++tmp.mat[i][next[i][j]];
    		}
    		REP(i, 0, L + 1) tmp.mat[i][L] = 1;
    		return tmp;
    	}
    } AC;
    
    int main()
    {
    	while (cin >> N >> L) {
    		AC.Init();
    		FOR(i, 1, N) {
    			char str[MAX_N]; cin >> str;
    			AC.Insert(str);
    		}
    		AC.Build();
    		Matrix a = AC.getMatrix();
    		a = Pow(a, L);
    		ULL ans(0);
    		REP(i, 0, a.n) ans += a.mat[0][i];
    		ans -= 1;
    
    		a = Matrix(2);
    		a.mat[0][0] = 26, a.mat[0][1] = a.mat[1][1] = 1;
    		a = Pow(a, L);
    		ULL sum = a.mat[0][0] + a.mat[0][1];
    		sum -= 1;
    
    		cout << (sum - ans) << endl;
    	}
    	return 0;
    }
    
    
    

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2825

    思路:建立AC自动机,每个节点都代表一种状态,建立fail指针的时候顺便更新以该节点结尾的字符串代表的状态。

    dp[i][j][k]代表最终密码长度为i,以AC自动机的位置j所在的字符结尾的,并且状态为k的密码的个数。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    #define REP(i, a, b) for (int i = (a); i < (b); ++i)
    #define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
    using namespace std;
    
    const int MAX_N = (10 * 10 + 10);
    const int MOD = (20090717);
    int N, M, K;
    int num[1 << 11], dp[27][MAX_N][1 << 11]; //dp[i][j][k]表示长度为i,在AC自动机上的节点为L,并且状态为k的符合条件的个数
    
    struct Trie {
    	int next[MAX_N][26], end[MAX_N], fail[MAX_N];
    	int root, L;
    	void Init() {
    		L = 0;
    		root = NewNode();
    	}
    	
    	int NewNode() {
    		REP(i, 0, 26) next[L][i] = -1;
    		end[L++] = 0;
    		return (L - 1);
    	}
    
    	void Insert(char *str, int ID) {
    		int len = strlen(str), now = root;
    		REP(i, 0, len) {
    			int index = str[i] - 'a';
    			if (next[now][index] == -1) next[now][index] = NewNode();
    			now = next[now][index];
    		}
    		end[now] |= (1 << ID);
    	}
    	
    	void Build() {
    		queue<int > que;
    		fail[root] = root;
    		REP(i, 0, 26) {
    			if (next[root][i] == -1) next[root][i] = root;
    			else {
    				fail[next[root][i]] = root;
    				que.push(next[root][i]);
    			}
    		}
    		while (!que.empty()) {
    			int now = que.front(); que.pop();
    			end[now] |= end[fail[now]];
    			REP(i, 0, 26) {
    				if (next[now][i] == -1) next[now][i] = next[fail[now]][i];
    				else {
    					fail[next[now][i]] = next[fail[now]][i];
    					que.push(next[now][i]);
    				}
    			}
    		}
    	}
    
    	int getDp() {
    		FOR(i, 0, N) {
    			REP(j, 0, L)
    				REP(s, 0, (1 << M)) dp[i][j][s] = 0;
    		}
    		dp[0][0][0] = 1;
    		REP(i, 0, N) {
    			REP(j, 0, L) {
    				REP(s, 0, (1 << M)) if (dp[i][j][s]) {
    					REP(k, 0, 26) {
    						dp[i + 1][next[j][k]][s | end[next[j][k]]] += dp[i][j][s];
    						if (dp[i + 1][next[j][k]][s | end[next[j][k]]] >= MOD) dp[i + 1][next[j][k]][s | end[next[j][k]]] %= MOD;
    					}
    				}
    			}
    		}
    		int ans = 0;
    		REP(s, 0, (1 << M)) if (num[s] >= K) {
    			REP(i, 0, L) {
    				ans += dp[N][i][s];
    				if (ans >= MOD) ans %= MOD;
    			}
    		}
    		return ans;
    	}
    
    } AC;
    
    
    
    int main()
    {
    	memset(num, 0, sizeof(num));
    	REP(s, 0, (1 << 12)) {
    		REP(i, 0, 12) if (s & (1 << i)) ++num[s];
    	}
    	while (cin >> N >> M >> K) {
    		if (N == 0 && M == 0 && K == 0) break;
    		AC.Init();
    		REP(i, 0, M) {
    			char str[11]; cin >> str;
    			AC.Insert(str, i);
    		}
    		AC.Build();
    		printf("%d
    ",AC.getDp());
    		
    	}
    	return 0;
    }
    

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2296

    思路:建立Trie,  end节点代表的该串的价值,dp[i][j]表示长度为i,在自动机上的位置为j的子串对应的价值。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <string>
    #include <queue>
    #define REP(i, a, b) for (int i = (a); i < (b); ++i)
    #define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
    using namespace std;
    
    const int MAX_N = (1100 + 100);
    int N, M, val[110];
    int dp[55][MAX_N]; //dp[i][j] : 长度为i,以AC自动机上的位置j的字符为结尾的串的最大价值
    string str[55][MAX_N]; 
    
    bool cmp(const string &str1, const string &str2)
    {
    	if ((int)str1.size() != (int)str2.size()) return (int)str1.size() < (int)str2.size();
    	return str1 < str2;
    }
    
    
    struct Trie {
    	int next[MAX_N][26], end[MAX_N], fail[MAX_N];
    	int root, L;
    
    	void Init() {
    		L = 0;
    		root = NewNode();
    	}
    
    	int NewNode() {
    		REP(i, 0, 26) next[L][i] = -1;
    		end[L++] = -1;
    		return (L - 1);
    	}
    
    	void Insert(char *str, int ID) {
    		int len = strlen(str), now = root;
    		REP(i, 0, len) {
    			int index = str[i] - 'a';
    			if (next[now][index] == -1) next[now][index] = NewNode();
    			now = next[now][index];
    		}
    		end[now] = ID;
    	}
    
    	void Build() {
    		queue<int > que;
    		fail[root] = root;
    		REP(i, 0, 26) {
    			if (next[root][i] == -1) next[root][i] = root;
    			else {
    				fail[next[root][i]] = root;
    				que.push(next[root][i]);
    			}
    		}
    		while (!que.empty()) {
    			int now = que.front(); que.pop();
    			REP(i, 0, 26) {
    				if (next[now][i] == -1) next[now][i] = next[fail[now]][i];
    				else {
    					fail[next[now][i]] = next[fail[now]][i];
    					que.push(next[now][i]);
    				}
    			}
    		}
    	}
    
    	void getAns() {
    		string ans("");
    		int maxval = 0, vv = 0;
    		memset(dp, -1, sizeof(dp));
    		dp[0][0] = 0;
    		str[0][0] = "";
    		REP(i, 0, N) {
    			REP(j, 0, L) if (dp[i][j] >= 0) {
    				REP(k, 0, 26) {
    					string tmp = str[i][j];
    					tmp += (char)('a' + k);
    					vv = dp[i][j];
    					if (end[next[j][k]] != -1) vv += val[end[next[j][k]]];
    					if (dp[i + 1][next[j][k]] < vv || (dp[i + 1][next[j][k]] == vv && cmp(tmp, str[i + 1][next[j][k]]))) {
    						dp[i + 1][next[j][k]] = vv;
    						str[i + 1][next[j][k]] = tmp;
    						if (vv > maxval || (vv == maxval && cmp(tmp, ans))) {
    							ans = tmp;
    							maxval = vv;
    						}
    					}
    				}
    			}
    		}
    		cout << ans << endl;
    	}
    
    } AC;
    
    int main()
    {
    	int Cas;
    	cin >> Cas;
    	while (Cas--) {
    		cin >> N >> M;
    		AC.Init();
    		REP(i, 0, M) {
    			char str[20]; cin >> str;
    			AC.Insert(str, i);
    		}
    		REP(i, 0, M) cin >> val[i];
    		AC.Build();
    		AC.getAns();
    	}
    	return 0;
    }


    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2457

    思路:dp[i][j]表示长度为i,在AC自动机上的位置为j满足条件的所需替换的字符的个数的最小值。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <string>
    #include <queue>
    #define REP(i, a, b) for (int i = (a); i < (b); ++i)
    #define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
    using namespace std;
    
    const int MAX_N = (1000 + 10);
    template < typename T > inline T getMIN(const T &a, const T &b)
    {
    	return a < b ? a : b;
    }
    int N, dp[MAX_N][MAX_N]; //len, pos;
    char str[MAX_N];
    
    
    struct Trie {
    	int next[MAX_N][4], end[MAX_N], fail[MAX_N];
    	int L, root;
    	
    	void Init() {
    		L = 0;
    		root = NewNode();
    	}
    
    	int NewNode() {
    		REP(i, 0, 4) next[L][i] = -1;
    		end[L++] = 0;
    		return (L - 1);
    	}
    
    	int getchange(char ch)  {
    		if (ch == 'A') return 0;
    		if (ch == 'C') return 1;
    		if (ch == 'G') return 2;
    		if (ch == 'T') return 3;
    		return -1;
    	}
    
    	void Insert(char *str) {
    		int len = strlen(str), now = root;
    		REP(i, 0, len) {
    			int id = getchange(str[i]);
    			if (next[now][id] == -1) next[now][id] = NewNode();
    			now = next[now][id];
    		}
    		end[now] = 1;
    	}
    	
    	void Build() {
    		queue<int > que;
    		fail[root] = root;
    		REP(i, 0, 4) {
    			if (next[root][i] == -1) next[root][i] = root;
    			else {
    				fail[next[root][i]] = root;
    				que.push(next[root][i]);
    			}
    		}
    		while (!que.empty()) {
    			int now = que.front(); que.pop();
    			if (end[fail[now]]) end[now] = 1;
    			REP(i, 0, 4) {
    				if (next[now][i] == -1) next[now][i] = next[fail[now]][i];
    				else {
    					fail[next[now][i]] = next[fail[now]][i];
    					que.push(next[now][i]);
    				}
    			}
    		}
    	}
    	
    	int getDp(char *str) {
    		memset(dp, 0x3f, sizeof(dp));
    		int len = strlen(str);
    		dp[0][0] = 0;
    		REP(i, 0, len) {
    			REP(j, 0, L) if (!end[j] && dp[i][j] < 0x3f3f3f3f){
    				REP(k, 0, 4) if (!end[next[j][k]]) {
    					int val = (k != getchange(str[i]));
    					dp[i + 1][next[j][k]] = getMIN(dp[i + 1][next[j][k]], dp[i][j] + val);
    				}
    			}
    		}
    		int ans = 0x3f3f3f3f;
    		REP(i, 0, L) ans = getMIN(ans, dp[len][i]);
    		if (ans == 0x3f3f3f3f) return -1;
    		return ans;
    	}
    } AC;
    
    int main()
    {
    	int t = 1;
    	while (cin >> N && N) {
    		AC.Init();
    		REP(i, 0, N) {
    			char ss[22]; cin >> ss;
    			AC.Insert(ss);
    		}
    		cin >> str;
    		AC.Build();
    		printf("Case %d: %d
    ", t++, AC.getDp(str));
    	}
    	return 0;
    }


  • 相关阅读:
    No module named scrapy 成功安装scrapy,却无法import的解决方法
    linux装sqlite3
    linux python3 安装scrapy 后提示 -bash: scrapy: 未找到命令
    使用splash爬去JavaScript动态请求的内容
    python之auto鼠标/键盘事件
    python分割txt文件
    [WPF]使用WindowChrome自定义Window Style
    [UWP]浅谈按钮设计
    [UWP]用Shape做动画(2):使用与扩展PointAnimation
    [UWP]用Shape做动画
  • 原文地址:https://www.cnblogs.com/wally/p/4477077.html
Copyright © 2020-2023  润新知