zoukankan      html  css  js  c++  java
  • AC自动机

    AC自动机, (Aho-Corasick automaton).
    AC自动机是什么?
        是在Trie树上建边,形成的一个图的算法.
    AC自动机用来干什么?
        构建状态转移的图.(维护"路径"!!)
            1. 维护路径是否可以走.
            2. 维护路径的值.
    如何构建AC自动机.
        1. 先建立一棵Trie树.
        2. 在Trie树上建我们需要的边.维护节点信息.
    
    
    Solved    1 / 1    A    HDU 2222    Keywords Search
    题意: 给定n个模式串,然后给一个文本串,问出现了多少个文本串.
          (串只有小写字母)
          N <= 10000 |S|<50
          |S|<1000000
    分析: 裸的AC自动机题.
    #include <queue>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    const int maxn = 1e6+500;
    
    char str[maxn];
    
    class AC {
        int next[maxn][26];
        int fail[maxn];
        int ed[maxn];
        int last[maxn];
        int qsz, root;
        
        
        inline int getid(char ch) { return ch - 'a'; }
        inline int newnode() {
            int i;
            for (i=0; i<26; ++i) 
                next[qsz][i] = 0;
            fail[qsz] = last[qsz] = ed[qsz] = 0;
            return qsz++;
        }
        
    public:
        void init() {
            qsz = 0;
            root = newnode();
        }
        
        void insert(char s[]) {
            int i, id, rt = 0, len = strlen(s);
            for (i=0; i<len; ++i) {
                id = getid(s[i]);
                if (!next[rt][id]) next[rt][id] = newnode();
                rt = next[rt][id];
            }
            ed[rt]++;
        }
        
        void build() {
            queue<int> q;
            int rt, i;
            fail[root] = last[root] = rt = root;
            for (i=0; i<26; ++i) 
                if (next[rt][i]) 
                    q.push(next[rt][i]);
            while (!q.empty()) {
                rt = q.front(); q.pop();
                for (i=0; i<26; ++i) {
                    if (!next[rt][i]) next[rt][i] = next[fail[rt]][i];
                    else {
                        fail[next[rt][i]] = next[fail[rt]][i];
                        last[next[rt][i]] = ed[fail[next[rt][i]]] ? fail[next[rt][i]] : last[next[rt][i]];
                        q.push(next[rt][i]);
                    }
                }
            }
        }
        int query(char s[]) {
            int rt = 0, tmp, i, len = strlen(s);
            int res = 0;
            for (i=0; i<len; ++i) {
                tmp = next[rt][getid(s[i])];
                while (tmp) {
                    res += ed[tmp];
                    ed[tmp] = 0;
                    tmp = last[tmp];
                }
                rt = next[rt][getid(s[i])];
            }
            return res;
        }
    }ac;
    
    int main()
    {
        int t, n, i;
        scanf("%d", &t);
        while (t--) {
            ac.init();
            scanf("%d", &n);
            for (i=0; i<n; ++i) {
                scanf("%s", str);
                ac.insert(str);    
            }
            ac.build();
            scanf("%s", str);
            printf("%d
    ", ac.query(str));
        }
        
        return 0;
    }
    View Code
    
    
    
    Solved    1 / 2    B    HDU 2896    病毒侵袭
    题意: 给定n个模式串标号为1..n  m个文本串,
          问每个文本串中出现模式串的标号,最后统计有出现模式串的文本串个数.
          每个文本串不会出现超过3个模式串.
          (注意ASCII的范围是可见字符)
          1<=N<=500 20<|S|<200
          1<=M<=1000 2000<|S|<10000
    分析: 还是裸的AC自动机.对于每个文本串,我们可以用一个set来记录出现的模式串.
    #include <set>
    #include <queue>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    const int maxn = 1e5+500;
    
    char str[maxn];
    
    set<int> se[1024];
    
    class AC {
        int next[maxn][128];
        int fail[maxn];
        int ed[maxn];
        int last[maxn];
        int qsz, root;
        
        inline int getid(char ch) { return ch - 32; }
        inline int newnode() {
            int i;
            for (i=0; i<128; ++i) 
                next[qsz][i] = 0;
            fail[qsz] = last[qsz] = ed[qsz] = 0;
            return qsz++;
        }
        
    public:
        void init() {
            qsz = 0;
            root = newnode();
        }
        
        void insert(char s[], int tid) {
            int i, id, rt = 0, len = strlen(s);
            for (i=0; i<len; ++i) {
                id = getid(s[i]);
                if (!next[rt][id]) next[rt][id] = newnode();
                rt = next[rt][id];
            }
            ed[rt] = tid;
        }
        
        void build() {
            queue<int> q;
            int rt, i;
            fail[root] = last[root] = rt = root;
            for (i=0; i<128; ++i) 
                if (next[rt][i]) 
                    q.push(next[rt][i]);
            while (!q.empty()) {
                rt = q.front(); q.pop();
                for (i=0; i<128; ++i) {
                    if (!next[rt][i]) next[rt][i] = next[fail[rt]][i];
                    else {
                        fail[next[rt][i]] = next[fail[rt]][i];
                        last[next[rt][i]] = ed[fail[next[rt][i]]] ? fail[next[rt][i]] : last[next[rt][i]];
                        q.push(next[rt][i]);
                    }
                }
            }
        }
        bool query(char s[], set<int> &se) {
            int rt = 0, tmp, i, len = strlen(s);
            bool ishave = false;
            for (i=0; i<len; ++i) {
                tmp = next[rt][getid(s[i])];
                while (tmp) {
                    if (ed[tmp]) 
                        se.insert(ed[tmp]), ishave = true;
                    tmp = last[tmp];
                }
                rt = next[rt][getid(s[i])];
            }
            return ishave;
        }
    }ac;
    
    int main()
    {
        int t, n, i;
        ac.init();
        scanf("%d", &n);
        for (i=1; i<=n; ++i) {
            scanf("%s", str);
            ac.insert(str, i);    
        }
        ac.build();
        scanf("%d", &n);
        int tot = 0;
        for (i=1; i<=n; ++i) {
            scanf("%s", str);
            tot += ac.query(str, se[i]);
        }
        for (i=1; i<=n; ++i) {
            if (se[i].empty()) continue;
            printf("web %d: %d", i, *se[i].begin());
            se[i].erase(se[i].begin());
            for (auto it : se[i]) printf(" %d", it);
            printf("
    ");
        }
        printf("total: %d
    ", tot);
        
        return 0;
    }
    View Code
    
    
    
    Solved    1 / 2    C    HDU 3065    病毒侵袭持续中
    题意: 给n个模式串, 1个文本串, 问每个模式串在文本串中出现的次数
          最后输出出现过的模式串 和 出现次数.
          (注意ASCII的范围是可见字符--
          当然,这个题也可以处理下,然后就只有大写字母,
          因为模式串只有大写字母: 文本串我们只需要把它分割,
          以每一段仅由大写字母组成的作为文本串进行匹配,不过没有卡空间,就随便啦.{我没被卡})
          1<=N<=1000 |S|<50
          |S|<2e6
    分析: 又是板子题.和上一题类似的.
    #include <queue>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    const int maxn = 2e6+500;
    const int cmaxn = 1e5;
    
    char str[maxn];
    char ss[1024][52];
    int ans[1024];
    
    class AC {
    public:
        int next[cmaxn][128];
        int fail[cmaxn];
        int ed[cmaxn];
        int last[cmaxn];
        int qsz, root;
        
        inline int getid(char ch) { return ch - 32; }
        inline int newnode() {
            int i;
            for (i=0; i<128; ++i) 
                next[qsz][i] = 0;
            fail[qsz] = last[qsz] = ed[qsz] = 0;
            return qsz++;
        }
    
        void init() {
            qsz = 0;
            root = newnode();
        }
        
        void insert(char s[], int tid) {
            int i, id, rt = 0, len = strlen(s);
            for (i=0; i<len; ++i) {
                id = getid(s[i]);
                if (!next[rt][id]) next[rt][id] = newnode();
                rt = next[rt][id];
            }
            ed[rt] = tid;
        }
        
        void build() {
            queue<int> q;
            int rt, i;
            fail[root] = last[root] = rt = root;
            for (i=0; i<128; ++i) 
                if (next[rt][i]) 
                    q.push(next[rt][i]);
            
            while (!q.empty()) {
                rt = q.front(); q.pop();
                for (i=0; i<128; ++i) {
                    if (!next[rt][i]) next[rt][i] = next[fail[rt]][i];
                    else {
                        fail[next[rt][i]] = next[fail[rt]][i];
                        last[next[rt][i]] = ed[fail[next[rt][i]]] ? fail[next[rt][i]] : last[next[rt][i]];
                        q.push(next[rt][i]);
                    }
                }
            }
        }
        int query(char s[]) {
            int rt = 0, tmp, i, len = strlen(s);
            for (i=0; i<len; ++i) {
                rt = tmp = next[rt][getid(s[i])];
                while (tmp) {
                    if (ed[tmp]) 
                        ans[ed[tmp]]++;
                    tmp = last[tmp];
                }
            }
        }
    }ac;
    
    int main()
    {
        int t, n, i;
        while (~scanf("%d", &n)) {
            ac.init();    
            memset(ans, 0, sizeof(ans));
            for (i=1; i<=n; ++i) {
                scanf("%s", ss[i]);
                ac.insert(ss[i], i);    
            }
            ac.build();
            scanf("%s", str);
            ac.query(str);
            for (i=1; i<=n; ++i) if (ans[i]) 
                printf("%s: %d
    ", ss[i], ans[i]);
        }
        
        return 0;
    }
    View Code
    
    

    Solved
    1 / 7 D ZOJ 3430 Detect the Virus 题意: 给定n个Base编码后的模式串,m个Base编码的文本串, 输出在文本串中出现的模式串的个数. 0 <= N <= 512 1 <= M <= 128 分析: 将编码解码后就是一道裸的AC自动机的题了.
    #include <cstdio>
    #include <queue>
    #include <map>
    #include <set>
    using namespace std;
    
    map<int, int> ma;
    int pwr[] = {1, 2, 4, 8, 16, 32, 64, 128};
    
    void init() {
        int i, id = 0;
        for (i='A'; i<='Z'; ++i) 
            ma[i] = id++;
        for (i='a'; i<='z'; ++i)
            ma[i] = id++;
        for (i='0'; i<='9'; ++i)
            ma[i] = id++;
        ma['+'] = id++;
        ma['/'] = id++;
    }
    
    char str[100086];
    int bits[100086];
    int ssss[100086];
    
    int get(char s[]) {
        int i, j, now = 0, cnt = 0, len = 0;
        for (i=0; s[i]; ++i) {
            if (s[i] == '=') cnt++;
            else for (j=0; j<6; ++j) bits[++now] = ma[s[i]] & (1 << (5 - j)) ? 1 : 0;
        }
        len = i;
        now -= cnt * 2;
        len = now / 8;
        now = 0;
        int tlen = 0, tmp;
        for (i=0; i<len; ++i) {
            tmp = 0;
            for (j=0; j<8; ++j) 
                tmp += bits[++now] * pwr[7-j];
            ssss[tlen++] = tmp;
        }
        return tlen;
    }
    
    const int maxn = 50000;
    
    class AC {
        int next[maxn][256];
        int fail[maxn];
        int ed[maxn];
        int last[maxn];
        int qsz, root;
    
        inline int newnode() {
            int i;
            for (i=0; i<256; ++i) 
                next[qsz][i] = 0;
            fail[qsz] = last[qsz] = ed[qsz] = 0;
            return qsz++;
        }
        
    public:
        void init() {
            qsz = 0;
            root = newnode();
        }
        
        void insert(int s[], int len) {
            int i, id, rt = 0;
            for (i=0; i<len; ++i) {
                id = s[i];
                if (!next[rt][id]) next[rt][id] = newnode();
                rt = next[rt][id];
            }
            ed[rt]++;
        }
        
        void build() {
            int i, rt, id;
            queue<int> q;
            rt = root;
            fail[root] = last[root] = rt = root;
            for (i=0; i<256; ++i) 
                if (next[rt][i]) 
                    q.push(next[rt][i]);
            
            while (!q.empty()) {
                rt = q.front(); q.pop();
                for (i=0; i<256; ++i) {
                    if (!next[rt][i]) next[rt][i] = next[fail[rt]][i];
                    else {
                        fail[next[rt][i]] = next[fail[rt]][i];
                        q.push(next[rt][i]); 
                        last[next[rt][i]] = ed[fail[next[rt][i]]] ? 
                                        fail[next[rt][i]] :
                                        last[next[rt][i]];
                    }
                }
            }
        }
    
        int query(int s[], int len) {
            int rt = 0, tmp, i;
            int res = 0;
            set<int> vis;
            for (i=0; i<len; ++i) {
                tmp = next[rt][s[i]];
                while (tmp) {
                    if (ed[tmp]) {
                        if (vis.find(tmp) == vis.end())
                            vis.insert(tmp);    
                    }
                    tmp = last[tmp];
                }
                rt = next[rt][s[i]];
            }
            for (set<int>::iterator iter=vis.begin(); iter!=vis.end(); ++iter) res += ed[*iter];
            return res;
        }
    }ac;
    
    int main()
    {
    //    freopen("E:\input.txt", "r", stdin);
        int i, n, len;
        bool flag = true; 
        init();
        while (~scanf("%d", &n)) {
    
            ac.init();
            for (i=0; i<n; ++i) {
                scanf("%s", str);
                len = get(str);    
                ac.insert(ssss, len);
            }
            ac.build(); 
            scanf("%d", &n);
            for (i=0; i<n; ++i) {
                scanf("%s", str);
                len = get(str);
                printf("%d
    ", ac.query(ssss, len));
            }
            printf("
    ");
        }
        
        return 0;
    }
    View Code
    
    

    Solved
    1 / 1 E POJ 2778 DNA Sequence 题意: 给定m个由ATGC组成的模式串, 问一个长度为n的由ATGC组成的串不含任何一个模式串的种类数. (0 <= m <= 10), |S|<10 n (1 <= n <=2000000000) 分析: 如果n比较小的情况下: 我们可以先建立AC自动机,然后建模式串的末尾节点标记, 同时他们的fail指针也进行标记.(被标记的节点说明是不可走的.) 然后我们从根节点出发,如果下个节点是标记过的节点, 那么说明是不可走的,我们就跳过他.然后继续走. 最后当我们走到长度为n时,我们的答案也出来了. ----> 我们在走的过程中累加答案, dp[length][state] 表示在长度为length,状态为state下的方案数. dp[length+1][nex_state] += dp[length][state]; 最后答案就是: sigma(dp[n][0...qsz]) , qsz是状态的总数. 但是,这道题的n非常大. 所以我们需要考虑一些加速的办法. n有2e9. 可以用矩阵快速幂来加速. 我们知道, 对于一个邻接矩阵A(离散数学上有,在矩阵那一章.), A^0表示走长度为0的方案数, A^1表示走长度为1的方案数, .... A^n表示走长度为n的发案数. 例如从点1出发到点10走n步 那么答案应该是 T = (A^n) ans = T[1][10]; 所以我们将我们可以走到的状态构建成一个邻接矩阵,然后快速幂. 最后答案就是sigma(A[0][0..qsz])
    #include <cstdio>
    #include <queue>
    #include <cstring>
    
    using namespace std;
    
    typedef long long ll;
    const ll mod = 100000;
    
    class Matrix {
    public:
        int r, c;
        ll mat[128][128];
        
        ll *operator [] (int x) { return mat[x]; }
        Matrix operator * (const Matrix &a) const {
            Matrix res;
            res.r = r; res.c = a.c;
            int i, j, k;
            for (i=0; i<res.r; ++i) {
                for (j=0; j<res.c; ++j) {
                    res[i][j] = 0;
                    for (k=0; k<c; ++k) 
                        res[i][j] = (res[i][j] + mat[i][k] * a.mat[k][j]) % mod;
                }
            }
            return res;
        } 
    }mt;
    
    Matrix pwr(const Matrix &a, int k) {
        Matrix base = a, r;
        int i, j;
        r.r = a.r; r.c = a.c;
        for (i=0; i<r.r; ++i)
            for (j=0; j<r.c; ++j)
                r[i][j] = i==j;
        while (k) {
            if (k & 1) r = r * base;
            base = base * base;
            k >>= 1;
        }
        return r;
    }
    
    class AC {
    public:
        int next[256][4];
        int ed[256];
        int fail[256];
        int qsz, root;
        
        inline int getid(char ch) {
            int res;
            switch (ch) {
            case 'A': res = 0; break;        
            case 'C': res = 1; break;        
            case 'G': res = 2; break;        
            case 'T': res = 3; break;        
            }
            return res; 
        }
        inline int newnode() {
            int i;
            for (i=0; i<4; ++i) 
                next[qsz][i] = 0;
            ed[qsz] = fail[qsz] = 0;
            return qsz++;
        }
    
        void init() {
            qsz = 0;
            root = newnode();
        }
        void insert(char s[]) {
            int i, id, rt = 0, len = strlen(s);
            for (i=0; i<len; ++i) {
                id = getid(s[i]);
                if (!next[rt][id]) next[rt][id] = newnode(); 
                rt = next[rt][id];
            }
            ed[rt]++;
        }    
        void build() {
            int rt, i;
            queue<int> q;
            rt = fail[root] = 0;
            for (i=0; i<4; ++i) {
                if (next[rt][i]) 
                    q.push(next[rt][i]);
            }
            while (!q.empty()) {
                rt = q.front(); q.pop();
                if (ed[rt]) { }
                if (ed[fail[rt]]) ed[rt] = 1;
                for (i=0; i<4; ++i) {
                    if (!next[rt][i]) next[rt][i] = next[fail[rt]][i];
                    else {
                        fail[next[rt][i]] = next[fail[rt]][i];
                        q.push(next[rt][i]);
                    }
                }
            }
        }
        void make() {
            memset(&mt, 0, sizeof(Matrix));
            int i, j;
            mt.r = mt.c = qsz;
            for (i=0; i<qsz; ++i) {
                for (j=0; j<4; ++j) {
                    if (ed[next[i][j]]) continue;
                    mt[i][next[i][j]]++; 
                }
            } 
        }
    }ac;
    
    char str[16];
    
    int main()
    {
    //    freopen("E:\input.txt", "r", stdin);
        int i, n, m;
        while (~scanf("%d%d", &m, &n)) {
            memset(&mt, 0, sizeof(mt));
            ac.init();
            for (i=1; i<=m; ++i) {
                scanf("%s", str);
                ac.insert(str);        
            }
            ac.build();
            ac.make();
            mt = pwr(mt, n);
            int res = 0;
            for (i=0; i<ac.qsz; ++i) res = (res + mt[0][i] + mod) % mod;
            printf("%d
    ", res);        
        }
        return 0;
    } 
    View Code
    
    
    
    
    Solved    1 / 1    F    HDU 2243    考研路茫茫――单词情结
    题意: 给定n个模式串,问长度为1...L中的串出现任一模式串的个数.
           0<n<6 |S|<=5
           0<L<2^31
    分析: 从上一题我们知道如何求 不出现模式串 长度为n的串 有多少种.
          而这个题是要我们求 出现任一个模式串的文本串的个数.
          我们先考虑长度为n的串有多少种可能:
          很明显  出现任一模式串的文本串个数 = 文本串种类数 - 没出现任一模式串的文本串个数.
          所以我们就可以套用上一题的解法,把 没出现任一模式串的文本串个数 求出.
          所以对于长度为n的,我们答案res_n已经求出了.
          因此 文本串长度从1...n的答案就是 res = res_1 + res_2 + res_3 + ... + res_n;
          但是, n非常大! 2^31, 如果只求一个,我们可以很快求出,如果求多个,我们复杂度得乘以一个n.
          明显是不可行的.
          但是,我们是通过矩阵实现的.所以我们先按照矩阵的写法把答案写出:
          Ans = A^1 + A^2 + A^3 + A^4 + ... + A^n;
          我们现在的问题转化成了, 如何快速的求出 A^1 + A^2 + A^3 + A^4 + ... + A^n 这个式子了.
          我们可以二分求和: 具体百度..或者看代码..一下就懂了.
          Matrix get(Matrix mat, int k) {
               if (k == 1) return mat;
               if (k & 1) return get(mat, k-1) + pwr(mat, k);
               else return (pwr(mat, k / 2) + pwr(mat, 0)) * get(mat, k / 2);
               // 注意对于矩阵,这儿用pwr(mat, 0)而不是把式子拆开.
          }
          (听说也可以在矩阵上添加改改然后可以求出.但是我不会.)
    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <iostream>
    
    using namespace std;
    
    typedef unsigned long long ull;
    
    class Matrix {
    public:
        int r, c;
        ull mat[32][32];
        
        ull *operator [] (int x) { return mat[x]; }
        Matrix operator * (const Matrix &a) const {
            Matrix res;
            res.r = r; res.c = a.c;
            int i, j, k;
            for (i=0; i<res.r; ++i) {
                for (j=0; j<res.c; ++j) {
                    res[i][j] = 0;
                    for (k=0; k<c; ++k) 
                        res[i][j] = (res[i][j] + mat[i][k] * a.mat[k][j]);
                }
            }
            return res;
        } 
        Matrix operator + (const Matrix &a) const {
            Matrix res;
            res.r = r; res.c = a.c;
            int i, j;
            for (i=0; i<res.r; ++i) 
                for (j=0; j<res.c; ++j) 
                    res[i][j] = (mat[i][j] + a.mat[i][j]);
            return res;
        }
    }mt;
    
    Matrix pwr(const Matrix &a, int k) {
        Matrix base = a, r;
        int i, j;
        r.r = a.r; r.c = a.c;
        for (i=0; i<r.r; ++i)
            for (j=0; j<r.c; ++j)
                r[i][j] = i==j;
        while (k) {
            if (k & 1) r = r * base;
            base = base * base;
            k >>= 1;
        }
        return r;
    }
    
    Matrix get(Matrix mat, int k) {
        if (k == 1) return mat;
        if (k & 1) return get(mat, k-1) + pwr(mat, k);
        else return (pwr(mat, k / 2) + pwr(mat, 0)) * get(mat, k / 2);  
    }
    
    ull pwr4(ull k) {
        ull base = 26*1llu, r = 1;
        while (k) {
            if (k & 1) r *= base;
            base *= base;
            k >>= 1; 
        }
        return r;
    }
    
    ull get(ull mat, ull k) {
        if (k == 1) return mat;
        if (k & 1) return get(mat, k-1) + pwr4(k);
        else return (pwr4(k / 2) + pwr4(0)) * get(mat, k / 2);  
    }
    
    
    class AC {
    public:
        int next[128][26];
        int ed[128];
        int fail[128];
        int qsz, root;
        
        inline int getid(char ch) {
            return ch - 'a'; 
        }
        inline int newnode() {
            int i;
            for (i=0; i<26; ++i) 
                next[qsz][i] = 0;
            ed[qsz] = fail[qsz] = 0;
            return qsz++;
        }
    
        void init() {
            qsz = 0;
            root = newnode();
        }
        void insert(char s[]) {
            int i, id, rt = 0, len = strlen(s);
            for (i=0; i<len; ++i) {
                id = getid(s[i]);
                if (!next[rt][id]) next[rt][id] = newnode(); 
                rt = next[rt][id];
            }
            ed[rt]++;
        }    
        void build() {
            int rt, i;
            queue<int> q;
            rt = fail[root] = 0;
            for (i=0; i<26; ++i) 
                if (next[rt][i]) 
                    q.push(next[rt][i]);
            while (!q.empty()) {
                rt = q.front(); q.pop();
                if (ed[fail[rt]]) ed[rt] = 1;
                for (i=0; i<26; ++i) {
                    if (!next[rt][i]) next[rt][i] = next[fail[rt]][i];
                    else {
                        fail[next[rt][i]] = next[fail[rt]][i];
                        q.push(next[rt][i]);
                    }
                }
            }
        }
        void make() {
            memset(&mt, 0, sizeof(Matrix));
            int i, j;
            mt.r = mt.c = qsz;
            for (i=0; i<qsz; ++i) {
                for (j=0; j<26; ++j) {
                    if (ed[next[i][j]]) continue;
                    mt[i][next[i][j]]++; 
                }
            } 
        }
    }ac;
    
    char str[16];
    
    int main()
    {
    //    freopen("E:\input.txt", "r", stdin);
        int i, n;
        ull m;
        while (cin >> n >> m) {
            memset(&mt, 0, sizeof(mt));
            ac.init();
            for (i=1; i<=n; ++i) {
                scanf("%s", str);
                ac.insert(str);         
            }
            ac.build();
            ac.make();
            mt = get(mt, m);
            ull res = get(26*1llu, m);
            for (i=0; i<ac.qsz; ++i) res -= mt[0][i];
            cout << res << endl;
        }
    
        return 0;
    } 
    View Code
    
    
    
    Solved    1 / 1    G    POJ 1625    Censored!
    题意: 有n个字符的字符集, p个禁止串(禁止出现的串),
          问一个长度为m的串不含禁止串的种类数.
          1 <= N <= 50
          1 <= M <= 50
          0 <= P <= 10  |S|<=10
    分析: 我们发现和E题就是同一个题. 但是!!!! 这个题,n特别小.
          所以,我们可以dp做,开心不?~~ 还有更开心的.这个题不用取模?
          2333 真的太贴心了不用取模.....2333
          什么意思呢? 你可以直接搞......才怪...你需要用大数....
          因为大数太耗内存了...用矩阵会炸,但是可以用dp做,参考上面E题的思路.然后套上大数板子就可以了.
    #include <iostream>
    #include <sstream>
    #include <cstring>
    #include <cstdio>
    #include <queue> 
    #include <map>
    
    using namespace std;
    
    typedef long long LL;
    
    #define MAX_L 90 
    
    class bign
    {
    public:
        int len, s[MAX_L];
        bign();
        bign(const char*);
        bign(int);
        bool sign;
        string toStr() const;
        friend istream& operator>>(istream &,bign &);
        friend ostream& operator<<(ostream &,bign &);
        bign operator=(const char*);
        bign operator=(int);
        bign operator=(const string);
        bool operator>(const bign &) const;
        bool operator>=(const bign &) const;
        bool operator<(const bign &) const;
        bool operator<=(const bign &) const;
        bool operator==(const bign &) const;
        bool operator!=(const bign &) const;
        bign operator+(const bign &) const;
        bign operator++();
        bign operator++(int);
        bign operator+=(const bign&);
        bign operator-(const bign &) const;
        bign operator--();
        bign operator--(int);
        bign operator-=(const bign&);
        bign operator*(const bign &)const;
        bign operator*(const int num)const;
        bign operator*=(const bign&);
        bign operator/(const bign&)const;
        bign operator/=(const bign&);
        bign operator%(const bign&)const;
        bign factorial()const;
        bign Sqrt()const;
        bign pow(const bign&)const;
        void clean();
        ~bign();
    };
    #define max(a,b) a>b ? a : b
    #define min(a,b) a<b ? a : b
    
    bign::bign()
    {
        memset(s, 0, sizeof(s));
        len = 1;
        sign = 1;
    }
    
    bign::bign(const char *num)
    {
        *this = num;
    }
    
    bign::bign(int num)
    {
        *this = num;
    }
    
    string bign::toStr() const
    {
        string res;
        res = "";
        for (int i = 0; i < len; i++)
            res = (char)(s[i] + '0') + res;
        if (res == "")
            res = "0";
        if (!sign&&res != "0")
            res = "-" + res;
        return res;
    }
    
    istream &operator>>(istream &in, bign &num)
    {
        string str;
        in>>str;
        num=str;
        return in;
    }
    
    ostream &operator<<(ostream &out, bign &num)
    {
        out<<num.toStr();
        return out;
    }
    
    bign bign::operator=(const char *num)
    {
        memset(s, 0, sizeof(s));
        char a[MAX_L] = "";
        if (num[0] != '-')
            strcpy(a, num);
        else
            for (int i = 1, tlen = strlen(num); i < tlen; i++)
                a[i - 1] = num[i];
        sign = !(num[0] == '-');
        len = strlen(a);
        for (int i = 0, tlen = strlen(a); i < tlen; i++)
            s[i] = a[len - i - 1] - 48;
        return *this;
    }
    
    bign bign::operator=(int num)
    {
        char temp[MAX_L];
        sprintf(temp, "%d", num);
        *this = temp;
        return *this;
    }
    
    bign bign::operator=(const string num)
    {
        const char *tmp;
        tmp = num.c_str();
        *this = tmp;
        return *this;
    }
    
    bool bign::operator<(const bign &num) const
    {
        if (sign^num.sign)
            return num.sign;
        if (len != num.len)
            return len < num.len;
        for (int i = len - 1; i >= 0; i--)
            if (s[i] != num.s[i])
                return sign ? (s[i] < num.s[i]) : (!(s[i] < num.s[i]));
        return !sign;
    }
    
    bool bign::operator>(const bign&num)const
    {
        return num < *this;
    }
    
    bool bign::operator<=(const bign&num)const
    {
        return !(*this>num);
    }
    
    bool bign::operator>=(const bign&num)const
    {
        return !(*this<num);
    }
    
    bool bign::operator!=(const bign&num)const
    {
        return *this > num || *this < num;
    }
    
    bool bign::operator==(const bign&num)const
    {
        return !(num != *this);
    }
    
    bign bign::operator+(const bign &num) const
    {
        if (sign^num.sign)
        {
            bign tmp = sign ? num : *this;
            tmp.sign = 1;
            return sign ? *this - tmp : num - tmp;
        }
        bign result;
        result.len = 0;
        int temp = 0;
        for (int i = 0; temp || i < (max(len, num.len)); i++)
        {
            int t = s[i] + num.s[i] + temp;
            result.s[result.len++] = t % 10;
            temp = t / 10;
        }
        result.sign = sign;
        return result;
    }
    
    bign bign::operator++()
    {
        *this = *this + 1;
        return *this;
    }
    
    bign bign::operator++(int)
    {
        bign old = *this;
        ++(*this);
        return old;
    }
    
    bign bign::operator+=(const bign &num)
    {
        *this = *this + num;
        return *this;
    }
    
    bign bign::operator-(const bign &num) const
    {
        bign b=num,a=*this;
        if (!num.sign && !sign)
        {
            b.sign=1;
            a.sign=1;
            return b-a;
        }
        if (!b.sign)
        {
            b.sign=1;
            return a+b;
        }
        if (!a.sign)
        {
            a.sign=1;
            b=bign(0)-(a+b);
            return b;
        }
        if (a<b)
        {
            bign c=(b-a);
            c.sign=false;
            return c;
        }
        bign result;
        result.len = 0;
        for (int i = 0, g = 0; i < a.len; i++)
        {
            int x = a.s[i] - g;
            if (i < b.len) x -= b.s[i];
            if (x >= 0) g = 0;
            else
            {
                g = 1;
                x += 10;
            }
            result.s[result.len++] = x;
        }
        result.clean();
        return result;
    }
    
    bign bign::operator * (const bign &num)const
    {
        bign result;
        result.len = len + num.len;
    
        for (int i = 0; i < len; i++)
            for (int j = 0; j < num.len; j++)
                result.s[i + j] += s[i] * num.s[j];
    
        for (int i = 0; i < result.len; i++)
        {
            result.s[i + 1] += result.s[i] / 10;
            result.s[i] %= 10;
        }
        result.clean();
        result.sign = !(sign^num.sign);
        return result;
    }
    
    bign bign::operator*(const int num)const
    {
        bign x = num;
        bign z = *this;
        return x*z;
    }
    bign bign::operator*=(const bign&num)
    {
        *this = *this * num;
        return *this;
    }
    
    bign bign::operator /(const bign&num)const
    {
        bign ans;
        ans.len = len - num.len + 1;
        if (ans.len < 0)
        {
            ans.len = 1;
            return ans;
        }
    
        bign divisor = *this, divid = num;
        divisor.sign = divid.sign = 1;
        int k = ans.len - 1;
        int j = len - 1;
        while (k >= 0)
        {
            while (divisor.s[j] == 0) j--;
            if (k > j) k = j;
            char z[MAX_L];
            memset(z, 0, sizeof(z));
            for (int i = j; i >= k; i--)
                z[j - i] = divisor.s[i] + '0';
            bign dividend = z;
            if (dividend < divid) { k--; continue; }
            int key = 0;
            while (divid*key <= dividend) key++;
            key--;
            ans.s[k] = key;
            bign temp = divid*key;
            for (int i = 0; i < k; i++)
                temp = temp * 10;
            divisor = divisor - temp;
            k--;
        }
        ans.clean();
        ans.sign = !(sign^num.sign);
        return ans;
    }
    
    bign bign::operator/=(const bign&num)
    {
        *this = *this / num;
        return *this;
    }
    
    bign bign::operator%(const bign& num)const
    {
        bign a = *this, b = num;
        a.sign = b.sign = 1;
        bign result, temp = a / b*b;
        result = a - temp;
        result.sign = sign;
        return result;
    }
    
    bign bign::pow(const bign& num)const
    {
        bign result = 1;
        for (bign i = 0; i < num; i++)
            result = result*(*this);
        return result;
    }
    
    bign bign::factorial()const
    {
        bign result = 1;
        for (bign i = 1; i <= *this; i++)
            result *= i;
        return result;
    }
    
    void bign::clean()
    {
        if (len == 0) len++;
        while (len > 1 && s[len - 1] == '')
            len--;
    }
    
    bign bign::Sqrt()const
    {
        if(*this<0)return -1;
        if(*this<=1)return *this;
        bign l=0,r=*this,mid;
        while(r-l>1)
        {
            mid=(l+r)/2;
            if(mid*mid>*this)
                r=mid;
            else
                l=mid;
        }
        return l;
    }
    bign::~bign()
    {
    }
    
    bign pwr4(bign base, int k) {
        bign r = 1;
        while (k) {
            if (k & 1) r = r  * base;
            base = base * base;
            k >>= 1;
        }
        return r;
    }
    map<int, int> ma;
    
    class AC {
    public:
        int next[128][64];
        int fail[128];
        int ed[128];
        int qsz, root, qn;
        
        inline int newnode() {
            int i;
            for (i=0; i<qn; ++i) 
                next[qsz][i] = 0;
            fail[qsz] = ed[qsz] = 0;
            return qsz++;
        }
    
        void init(int n) {
            qsz = 0;
            qn = n;
            root = newnode();
        }
        void insert(string s) {
            int i, rt = 0, id;
            for (i=0; s[i]; ++i) {
                id = ma[s[i]];
                if (!next[rt][id]) next[rt][id] = newnode();    
                rt = next[rt][id];
            }
            ed[rt]++;
        }
        void build() {
            int i, rt;
            queue<int> q;
            rt = fail[root] = 0;
            for (i=0; i<qn; ++i) 
                if (next[rt][i]) 
                    q.push(next[rt][i]);
            while (!q.empty()) {
                rt = q.front(); q.pop();
                if (ed[fail[rt]]) ed[rt] = 1;
                for (i=0; i<qn; ++i) {
                    if (!next[rt][i]) next[rt][i] = next[fail[rt]][i];
                    else {
                        fail[next[rt][i]] = next[fail[rt]][i];
                        q.push(next[rt][i]);
                    }
                }
            }
        }
    }ac;
    
    bign dp[128][128];
    
    int main()
    {
    //    freopen("E:\input.txt", "r", stdin);
        int n, m, p, i, j, k;
        char str[64];
        while (cin >> n >> m >> p) {
            getchar();
            ma.clear();
            
            ac.init(n);
            gets(str);
            for (i=0; i<n; ++i) ma[str[i]] = i;
            for (i=0; i<p; ++i) {
                gets(str);
                ac.insert(str);
            }
            ac.build();
            int rt;
            for (i=0; i<=m; ++i) 
                for (j=0; j<ac.qsz; ++j) 
                    dp[i][j] = 0;
            dp[0][0] = 1;
            for (i=0; i<m; ++i) {
                for (j=0; j<ac.qsz; ++j) {
                    for (k=0; k<n; ++k) {
                        if (ac.ed[ac.next[j][k]]) continue;
                        dp[i+1][ac.next[j][k]] = dp[i+1][ac.next[j][k]] + dp[i][j];
                    }
                }
            }
            bign res = 0;
            for (i=0; i<ac.qsz; ++i) res = res + dp[m][i];
            cout << res << endl;
        }
        
        return 0;
    }
    View Code
    
    

    Solved
    1 / 5 H HDU 2825 Wireless Password 题意: 问一个长度为n的密码可能有多少种,条件: 1. 给定m个可能是密码的子串 2. 密码至少含k个可能的子串. 1<=n<=25 0<=m<=10 |S|<=10 分析: n比较小,我们考虑一下dp. k也是1...10的,所以我们可以有状压. 我们参考E G题的dp思路, 不难写出dp[length][state][k] length表示当前长度, state表示的是在AC自动机的状态,k是选取密码串的状态. 然后我们状态就好转移了: dp[length+1][nex_state][nex_k] += dp[length][state][k]; 最后答案是 sigma(dp[n][1..qsz][num>=k])
    #include <cstdio>
    #include <queue>
    #include <cstring>
    
    using namespace std;
    
    const int mod = 20090717;
    
    typedef int ll;
    
    ll dp[32][128][1024];
    int num[1024];
    
    class AC {
    public:
        int next[128][26];
        int fail[128];
        int ed[128];
        int qsz, root;
        
        int newnode() {
            int i;
            for (i=0; i<26; ++i) 
                next[qsz][i] = 0;
            fail[qsz] = ed[qsz] = 0;
            return qsz++;
        }
        
        inline int getid(char ch) { return ch - 'a'; }
        
        init() {
            qsz = 0;
            root = newnode();
        }
        
        void insert(char s[], int tid) {
            int i, rt = 0, id;
            for (i=0; s[i]; ++i) {
                id = getid(s[i]);
                if (!next[rt][id]) next[rt][id] = newnode();
                rt = next[rt][id];
            }
            ed[rt] |= (1 << tid);
        }
        
        void build() {
            int i, rt;
            queue<int> q;
            rt = root;
            for (i=0; i<26; ++i) 
                if (next[rt][i]) 
                    q.push(next[rt][i]);
            while (!q.empty()) {
                rt = q.front(); q.pop();
                if (ed[fail[rt]]) ed[rt] |= ed[fail[rt]];
                for (i=0; i<26; ++i) {
                    if (!next[rt][i]) next[rt][i] = next[fail[rt]][i];
                    else {
                        fail[next[rt][i]] = next[fail[rt]][i];
                        q.push(next[rt][i]);
                    }
                }
            }
        }
        
        ll get(int n, int m, int kk) {
            int i, j, k, d, z, st;
            memset(dp, 0, sizeof(dp));
            dp[0][0][0] = 1;
            for (z=0; z<(1<<m); ++z) {
                for (i=0; i<n; ++i) {
                    for (j=0; j<qsz; ++j) {
                        if (!dp[i][j][z]) continue;
                        for (d=0; d<26; ++d) {
                            st = z | ed[next[j][d]];
                            dp[i+1][next[j][d]][st] = (dp[i+1][next[j][d]][st] + dp[i][j][z]) % mod;
                        }
                    }
                }
            }
            ll res = 0;
            for (z=0; z<(1<<m); ++z) 
                if (num[z] < kk) continue;
                else for (i=0; i<qsz; ++i) 
                    res = (res + dp[n][i][z]) % mod;
            return res;
        }
    }ac;
    
    char str[32];
    
    int main()
    {
        int i, j, n, m, k, tmp, cnt;
        for (i=0; i<1024; ++i) {
            tmp = i;
            cnt = 0;
            while ((tmp & -tmp)) {
                tmp -= (tmp & -tmp);
                cnt++;
            }
            num[i] = cnt;
        }
        while (~scanf("%d%d%d", &n, &m, &k) && (n || m || k)) {
            ac.init();
            for (i=0; i<m; ++i) {
                scanf("%s", str);
                ac.insert(str, i);
            }
            ac.build();
            ll res;
            res = ac.get(n, m, k);
            printf("%d
    ", res);
        }
        
        return 0;
    }
    View Code
    
    

    Solved
    1 / 3 I HDU 2296 Ring 题意: 给定m个单词,每个单词都有价值,求长度为n的最大价值的单词. 如果有多个答案,取长度较小的,如果长度相同,则取字典序较小的. 0 < N ≤ 50 0 < M ≤ 100 |S|<=10 分析: 单词的最大值好求,dp,问题是记录路径. ...很暴力,我们直接用string存,然后比较就OK了. dp[length][state]
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #include <queue>
    #include <string>
    
    using namespace std;
    
    int val[128];
    
    char ans[5056][128];
    
    class AC {
    public:
        int next[5056][26];
        int fail[5056];
        int rcd[5056];
        int ed[5056];
        int qsz;
        
        int newnode() {
            int i;
            for (i=0; i<26; ++i)
                next[qsz][i] = 0;
            fail[qsz] = ed[qsz]    = 0;
            return qsz++;
        }
        
        void init() {
            qsz  = 0;
            newnode();
        }
        
        inline int getid(char ch) { return ch-'a'; }
        
        void insert(char s[], char tid) {
            int i, id, rt = 0;
            for (i=0; s[i]; ++i) {
                id = getid(s[i]);
                if (!next[rt][id]) next[rt][id] = newnode();
                rt = next[rt][id];
            }
            rcd[tid] = rt;
        }
        
        void build() {
            int rt, i;
            queue<int> q;
            rt = 0;
            for (i=0; i<26; ++i) 
                if (next[rt][i]) q.push(next[rt][i]);
            while (!q.empty()) {
                rt = q.front(); q.pop();
                ed[rt] += ed[fail[rt]];
                for (i=0; i<26; ++i) {
                    if (!next[rt][i]) next[rt][i] = next[fail[rt]][i];
                    else {
                        fail[next[rt][i]] = next[fail[rt]][i];
                        q.push(next[rt][i]);
                    }
                }
            }
        }
        
        int dp[64][5056];
        string ss[64][5056];
        void slove(int n) {
            int i, j, k;
            for (i=0; i<=n; ++i)
                for(j=0; j<qsz; ++j)
                    ss[i][j].clear();
            memset(dp, -1, sizeof(dp));
            dp[0][0] = 0;
            int nex, tmp;
            for (i=0; i<n; ++i) {
                for (j=0; j<qsz; ++j) {
                    if (dp[i][j] == -1) continue;
                    for (k=0; k<26; ++k) {
                        nex = next[j][k];
                        tmp = ed[nex] + dp[i][j];    
                        if (dp[i+1][nex] == tmp) {
                            if (ss[i+1][nex] > ss[i][j] + char(k + 'a')) {
                                ss[i+1][nex] = ss[i][j] + char(k + 'a');
                            }
                        } else if (dp[i+1][nex] < tmp){
                            dp[i+1][nex] = tmp;
                            ss[i+1][nex] = ss[i][j] + char(k + 'a');
                        }
                    }
                }
            }
            string ans;
            int ians = 0;
            for (i=n; i>=0; --i) {
                for (j=0; j<qsz; ++j) {
                    if (dp[i][j] > ians) {
                        ians = dp[i][j];
                        ans = ss[i][j];
                    } else if (dp[i][j] == ians) {
                        if (ss[i][j].size() < ans.size())
                            ans = ss[i][j];
                        else if (ss[i][j].size() == ans.size()) {
                            if (ss[i][j] < ans) ans = ss[i][j];
                        }
                    }
                }
            }
            if (ans.size() == 0) printf("
    ");
            else printf("%s
    ", ans.c_str());
        }
    }ac;
    
    int main()
    {
    //    freopen("E:\input.txt", "r", stdin);
        int t, n, m, i, tmp;
        char str[64];
        scanf("%d", &t);
        while (t--) {
            scanf("%d%d", &n, &m);    
            ac.init();
            for (i=1; i<=m; ++i) {
                scanf("%s", str);
                ac.insert(str, i);
            }
            for (i=1; i<=m; ++i) {
                scanf("%d", &tmp);
                ac.ed[ac.rcd[i]] += tmp;
            }
            ac.build();
            ac.slove(n);
        }
            
        return 0;
    }
    View Code
    
    
    
    Solved    1 / 5    J    HDU 2457    DNA repair
    题意: 给你n个模式串,一个文本串,然后可以让文本串的某一个字母修改.
          问至少要修改多少次,才不会出现任何一个模式串.
    分析: 假设我们的状态, dp[length][state]表示在长度为length,state状态时的最小值.
          然后我们可以跟着文本串走就OK了:
            即 if (走到下一个状态,经过原字符串的字符)
                    tmp = dp[length][state];
               else
                    tmp = dp[length][state] + 1;
            然后 dp[length+1][nex_state] = min(dp[length+1][nex_state], tmp);
    #include <cstdio>
    #include <cstring>
    #include <queue>
    #include <algorithm>
    
    using namespace std;
    
    class AC {
    public:
        int next[1024][4];
        int fail[1024];
        bool ed[1024];
        int qsz, root;
        
        inline int getid(char ch) {
            int res;
            switch (ch) {
            case 'A': res=0; break;
            case 'T': res=1; break;
            case 'G': res=2; break;
            case 'C': res=3; break;
            }
            return res; 
        } 
        
        int newnode() {
            int i;
            for (i=0; i<4; ++i) 
                next[qsz][i] = 0;
            fail[qsz] = ed[qsz] = false;
            return qsz++;
        }
        
        void init() {
            qsz = 0;
            root = newnode();
        }
        
        void insert(char s[]) {
            int i, rt = 0, id;
            for (i=0; s[i]; ++i) {
                id = getid(s[i]);
                if (!next[rt][id]) next[rt][id] = newnode();
                rt = next[rt][id];
            }
            ed[rt] = true;
        }
        void build() {
            int rt, i;
            queue<int> q;
            rt = root;
            for (i=0; i<4; ++i) 
                if (next[rt][i]) 
                    q.push(next[rt][i]);
            while (!q.empty()) {
                rt = q.front(); q.pop();
                ed[rt] |= ed[fail[rt]];
                for (i=0; i<4; ++i) {
                    if (!next[rt][i]) next[rt][i] = next[fail[rt]][i];
                    else {
                        fail[next[rt][i]] = next[fail[rt]][i];
                        q.push(next[rt][i]);
                    }
                }
            }
        }
        
        int dp[1024][1024];
        
        int getans(char s[]) {
            int res = 0x3f3f3f3f;
            int i, j, k, tmp;
            
            memset(dp, 0x3f, sizeof(dp));
            dp[0][0] = 0;
            for (i=0; s[i]; ++i) {
                for (j=0; j<qsz; ++j) {
                    if (ed[j]) continue;
                    for (k=0; k<4; ++k) {
                        if (ed[next[j][k]]) continue;
                        if (getid(s[i]) == k) tmp = dp[i][j];
                        else tmp = dp[i][j] + 1;
                        dp[i+1][next[j][k]] = min(dp[i+1][next[j][k]], tmp);
                    }
                }
            }
            for (j=0; j<qsz; ++j) res = min(res, dp[i][j]);
            return res == 0x3f3f3f3f ? -1 : res;
        }
    }ac;
    
    char str[1024];
    
    int main()
    {
    //    freopen("E:\input.txt", "r", stdin);
        int i, n, cas = 1;
        while (~scanf("%d", &n) && n) {
            ac.init();
            for (i=0; i<n; ++i) {
                scanf("%s", str);
                ac.insert(str);
            }
            ac.build();
            scanf("%s", str);
            printf("Case %d: %d
    ", cas++, ac.getans(str));
        }
        
        return 0;
    }
    View Code

    Solved
    1 / 12 K ZOJ 3228 Searching the String 题意: 给n个模式串, 然后查询模式串出现的次数. 有两种类型的查询: 0 支持重叠. 1 不支持重叠. |S|<6 |S|<10^5 分析: 裸的AC自动机题, 多于不重叠的,我们可以用一个lst[maxn]数组和deep[id]数组来维护. deep[state] = i ---> 在建立Trie树的时候维护,表示字符长度. lst[state]表示当前状态上一次出现的位置,在查询的时候更新. 如果i-lst[state]>=deep[state]说明不重叠,可以取值,同时更新lst[state]; .....不过这个题有个恶心的坑点: 他给的模式串可能一样!!即会给出多次.所以记录答案的时候要注意 (建Trie树的时候,维护同一个节点需要保存2个或者多个串答案的可能.)
    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <set>
    
    using namespace std;
    
    
    const int maxn = 1e5+50;
    const int cmaxn = maxn * 6;
    int ans[maxn];
    char ss[cmaxn];
    int fa[maxn];
    
    class AC{
    public:
        int next[cmaxn][26];
        int fail[cmaxn];
        int ed[cmaxn][2];
        int lst[cmaxn];
        
        int deep[cmaxn];
        int qsz, root;
        
        inline int getid(char ch) { return ch-'a'; }
        inline int newnode() {
            int i;
            for (i=0; i<26; ++i) 
                next[qsz][i] = 0;
            deep[qsz] = fail[qsz] = ed[qsz][0] = ed[qsz][1] = 0;
            return qsz++;
        }
    
        void init() {
            qsz = 0;
            root = newnode();
            memset(fa, 0, sizeof(fa));
        }
        
        void insert(char s[], int tid, int op) {
            int i, id, rt = 0, len = strlen(s);
            for (i=0; i<len; ++i) {
                id = getid(s[i]);
                if (!next[rt][id]) next[rt][id] = newnode();
                rt = next[rt][id];
                deep[rt] = i+1;
            }
            if (!ed[rt][op]) { ed[rt][op] = tid; }
            fa[tid] = ed[rt][op];
        }
        
        void build() {
            queue<int> q;
            int rt, i;
            fail[root] = rt = root;
            for (i=0; i<26; ++i) 
                if (next[rt][i]) 
                    q.push(next[rt][i]);
            while (!q.empty()) {
                rt = q.front(); q.pop();
                for (i=0; i<26; ++i) {
                    if (!next[rt][i]) next[rt][i] = next[fail[rt]][i];
                    else {
                        fail[next[rt][i]] = next[fail[rt]][i];
                        q.push(next[rt][i]);
                    }
                }
            }
        }
        
        int query(char s[]) {
            int rt = 0, tmp, i, len = strlen(s);
            memset(lst, -1, sizeof(lst));
            for (i=0; i<len; ++i) {
                tmp = rt = next[rt][getid(s[i])];
                while (tmp) {
                    if (lst[tmp]==-1 || i-lst[tmp] >= deep[tmp]) {
                        ans[ed[tmp][1]]++;
                        lst[tmp] = i;
                    }
                    ans[ed[tmp][0]]++;        
                    tmp = fail[tmp];
                }
            }
        }
    }ac;
    
    int main()
    {
    //    freopen("E:\input.txt", "r", stdin);
        int n, i, op, cas=1;
        char str[128];
        
        while (~scanf("%s", ss)) {
            
            scanf("%d", &n);
            memset(ans, 0, sizeof(ans));
            ac.init();
            for (i=1; i<=n; ++i) {
                scanf("%d%s", &op, str);
                ac.insert(str, i, op);
            }
            ac.build(); ac.query(ss);
    
            printf("Case %d
    ", cas++);
            for (i=1; i<=n; ++i) printf("%d
    ", ans[i] ? ans[i] : ans[fa[i]]);
            printf("
    ");
        } 
        
        return 0;
    }
    View Code

    Solved
    1 / 2 L HDU 3341 Lost's revenge 题意: (都只含ATGC)给n个模式串,一个文本串,文本串字母顺序可以调整.问调整后,最多可以出现几个模式串.(可以重叠) 1<=N<=50 |S|<=10 |S| <= 40 分析: 如果是任意的长度为m的字符串,我们可以直接dp搞. 但是现在多了一些限制: 长度为m,同时ATGC的个数是固定的了. 所以,我们不妨令dp[length][state][num_A][num_T][num_G][num_C]来表示我们当前的状态可以获取的最大值. length表示当前长度, state表示的是在AC自动机的状态, num表示用了多少个. 所以,我们的状态可以转移了. 但是,我们发现,6维吓人了..开不下. 不过,我们发现length这一维可以剩,因为他和num那4维是等价的. 所以,我们现在有一个5维的dp num的范围是40,state是500 500*40^4还是开不下. 考虑状态压缩,我们可以把num那四维压缩. 我们可以乘以权值来压缩,这样可以保证不会重叠...然后最多只有30000多(我证不来...别人题解说的 2333) 权值: _h[0] = 1; _h[1] = (num[0] + 1); _h[2] = (num[0] + 1) * (num[1] + 1); _h[3] = (num[0] + 1) * (num[1] + 1) * (num[2] + 1); 然后我们的dp[state][tt], 只有两维了..
    #include <cstdio>
    #include <cstring>
    #include <queue>
    #include <algorithm>
    
    using namespace std;
    
    class AC {
    public:
        int next[528][4];
        int fail[528];
        int ed[528];
        int qsz, root;
        
        // ATGC
        inline int getid(char ch) {
            switch (ch) {
            case 'A': return 0;
            case 'T': return 1;
            case 'G': return 2;
            case 'C': return 3;
            }
        }
        
        inline int newnode() {
            int i;
            for (i=0; i<4; ++i) 
                next[qsz][i] = 0;
            ed[qsz] = fail[qsz] = 0;
            return qsz++;
        }
        
        void init() {
            qsz = 0;
            root = newnode();
        }
        
        void insert(char s[]) {
            int i, id, rt = 0;
            for (i=0; s[i]; ++i) {
                id = getid(s[i]);
                if (!next[rt][id]) next[rt][id] = newnode();
                rt = next[rt][id];
            }
            ed[rt]++;
        }
        
        void build() {
            int i, rt;
            queue<int> q;
            rt = 0;
            for (i=0; i<4; ++i) 
                if (next[rt][i])
                    q.push(next[rt][i]);
            
            while (!q.empty()) {
                rt = q.front(); q.pop();
                ed[rt] += ed[fail[rt]];
                for (i=0; i<4; ++i) {
                    if (!next[rt][i]) next[rt][i] = next[fail[rt]][i];
                    else {
                        fail[next[rt][i]] = next[fail[rt]][i];
                        q.push(next[rt][i]);
                    }
                }
            }
        }
        
        // ATGC
        int num[4];
        int _h[4];
        int dp[528][15555];
        int slove(char s[]) {
            int i, len;
            num[0] = num[1] = num[2] = num[3] = 0;
            for (len=0; s[len]; ++len) 
                num[getid(s[len])]++; 
            _h[0] = 1; 
            _h[1] = (num[0] + 1); 
            _h[2] = (num[0] + 1) * (num[1] + 1); 
            _h[3] = (num[0] + 1) * (num[1] + 1) * (num[2] + 1); 
                
            memset(dp, -1, sizeof(dp));
            dp[0][0] = 0;
            int A, T, G, C, k, od, ne;
            for (A=0; A<=num[0]; ++A) 
                for (T=0; T<=num[1]; ++T) 
                    for (G=0; G<=num[2]; ++G) 
                        for (C=0; C<=num[3]; ++C) 
                            for (i=0; i<qsz; ++i) {
                                od = A * _h[0] + T * _h[1] + G * _h[2] + C * _h[3];
                                if (dp[i][od] == -1) continue;
                                for (k=0; k<4; ++k) {
                                    if (k==0 && A==num[0]) continue;
                                    if (k==1 && T==num[1]) continue;
                                    if (k==2 && G==num[2]) continue;
                                    if (k==3 && C==num[3]) continue;
                                    switch (k) {
                                    case 0: ne = od + _h[0]; break;
                                    case 1: ne = od + _h[1]; break;
                                    case 2: ne = od + _h[2]; break;
                                    case 3: ne = od + _h[3]; break;
                                    }
                                    dp[next[i][k]][ne] = max(dp[i][od]+ed[next[i][k]], dp[next[i][k]][ne]);
                                }
                            }
            int res = 0;
            od = num[0] * _h[0] + num[1] * _h[1] + num[2] * _h[2] + num[3] * _h[3];
            for (i=0; i<qsz; ++i) res = max(res, dp[i][od]);
            return res;
        }
    }ac;
    
    char str[64];
    
    int main()
    {
    //    freopen("E:\input.txt", "r", stdin);
        int n, i, cas = 1;
        while (scanf("%d", &n) && n) {
            ac.init();
            for (i=0; i<n; ++i) {
                scanf("%s", str);
                ac.insert(str);
            }
            ac.build();
            scanf("%s", str);
            printf("Case %d: %d
    ", cas++, ac.slove(str));
        }     
        
        return 0;
    }
    View Code
    
    


    Solved
    1 / 2 M HDU 3247 Resource Archiver 题意: 给n个串和m个串,问n个串组成一个最短的新串,不含任一个m串 (2 <= n <= 10, 1 <= m <= 1000) |S|<=1000 分析: 我们定义dp[i][state]表示节点i到达state这个状态的最小值,
    state状态是选取n个串的情况,即状态压缩的情况,更新就行了.
    dist[i][j]表示从i个串到j个串的最短距离.更新dp[][]需要用到.
    dist[i][j]可以通过在AC自动机上bfs求得.
    #include <cstdio>
    #include <cstring>
    #include <queue>
    
    using namespace std;
    
    const int maxn = 60500;
    int n, m;
    class AC {
    public:
        int next[maxn][2];
        int fail[maxn];
        int good[maxn];
        int bad[maxn];
        int qsz, root, cnt;
        int pos[256];
        
        inline int getid(char ch) { return ch-'0'; }
        inline int newnode() {
            next[qsz][0] = next[qsz][1] = good[qsz] = bad[qsz] = fail[qsz] = 0;
            return qsz++;
        }
        
        init() {
            qsz = 0;
            cnt = 1;
            root = newnode();
        }
        
        void insert(char s[], int tid) {
            int i, id, rt = root;
            for (i=0; s[i]; ++i) {
                id = getid(s[i]);
                if (!next[rt][id]) next[rt][id] = newnode();
                rt = next[rt][id];
            }
            pos[0] = 0;
            if (tid >= 0) good[rt] |= (1 << tid), pos[cnt++] = rt;
            else bad[rt] = 1;
         }
        
        void build() {
            int i, rt = root;
            queue<int> q;
            for (i=0; i<2; ++i) 
                if (next[rt][i])
                    q.push(next[rt][i]);
            while (!q.empty()) {
                rt = q.front(); q.pop();
                good[rt] |= good[fail[rt]];
                bad[rt] |= bad[fail[rt]];
                for (i=0; i<2; ++i) {
                    if (!next[rt][i]) next[rt][i] = next[fail[rt]][i];
                    else {
                        fail[next[rt][i]] = next[fail[rt]][i];
                        q.push(next[rt][i]);
                    }
                }
            }
        }
        static const int inf = 0x3f3f3f3f;
        int dis[maxn];
        int path[256][256];
        void spfa(int st) {  // 其实这只是一个bfs,找最短路. 在AC自动机上bfs,找到两个单词的最短路. 
            queue<int> q;
            int i, rt, nex;
            q.push(pos[st]);
            memset(dis, 0x3f, sizeof(dis));
            dis[pos[st]] = 0;
            while (!q.empty()) {
                rt = q.front(); q.pop();
                for (i=0; i<2; ++i) {
                    nex = next[rt][i];
                    if (bad[nex]) continue;
                    if (dis[nex] == inf) {
                        dis[nex] = dis[rt] + 1;
                        q.push(nex);
                    }
                }
            }
            for (i=0; i<cnt; ++i) 
                path[st][i] = dis[pos[i]];
        }
        int dp[256][1024];
        int getans() {
            int i, tps, j, k, nex, state;
        /*    cnt = 0;  //获取所有需要连接上去的串的尾节点.  包括根节点. 因为是从根节点开始走的,所以需要包括根节点. 
            pos[cnt++] = 0;
            for (i=0; i<qsz; ++i) 
                if (good[i] && !bad[i])
                    pos[cnt++] = i;*/
        //    printf("%d 
    ", cnt);
            for (i=0; i<cnt; ++i) 
                spfa(i);
            memset(dp, 0x3f, sizeof(dp));
        //    for (i=0; i<n; ++i) // 是以0为起点,这个初始化时以任一节点为起点的. 
        //        dp[i][(1<<i)] = 0; 
            dp[0][0] = 0;
            tps = (1 << n) - 1;
            for (state=0; state<=tps; ++state) {
                for (i=0; i<cnt; ++i) {
                //    if ((state & (1 << i))) continue; 只能走一次. 
                //    if (dp[i][state] == inf) continue;
                    for (j=0; j<cnt; ++j) {
                    //    if ((state & (1 << j))) continue;  表示只能走一次. 但实际上可以走多次. 
                        nex = state | good[pos[j]];
                        dp[j][nex] = min(dp[j][nex], dp[i][state] + path[i][j]);
                    }
                }
            }
            int res = inf;
            for (i=0; i<cnt; ++i)
                res = min(dp[i][tps], res);
            return res;
        }
        
    }ac;
    
    
    char str[1024];
    int main()
    {
        int i;
    //    freopen("E:\input.txt", "r", stdin);
        while (scanf("%d%d", &n, &m) && (n || m)) {
            ac.init();
            for (i=0; i<n; ++i) {
                scanf("%s", str);
                ac.insert(str, i);
            }
            for (i=0; i<m; ++i) {
                scanf("%s", str);
                ac.insert(str, -1);
            }
            ac.build();
            printf("%d
    ", ac.getans());
         }
        
        return 0;
    }
    View Code
    然后这是一个类似的题,不过这个题要求只能走一遍.
    https://vjudge.net/problem/HDU-4856
    
    
    Solved    1 / 6    N    ZOJ 3494    BCD Code
    题意:  给你BCD编码: 1-9都对应一个4为的二进制,然后给定n个禁止出现的二进制
           数字对应转化后不能出现这些二进制,然后问区间内有多个满足数.
    分析:  关于区间内有多少个满足条件的数,一般我们直接考虑数位dp.
           然后在数位dp中,状态是否可以继续走下去,如何走下去,结束的条件是什么,这是需要我们考虑的.
           对于状态,我们发现那是路径,连续的状态,所以我们可以用AC自动机来构建.
           然后在dfs过程中把数字拆成二进制,同时状态进行更新后传递下去.
           然后...其实就是个板子题了..
           然后注意 A ≤ B < 10^200 所以读入要用字符串,然后对于左边区间要减一,也是需要处理的.
    #include <queue>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    typedef long long ll;
    
    const int mod = 1000000009;
    
    class AC {
    public:
        int next[2048][2];
        int fail[2048];
        bool ed[2048];
        int qsz, root;
        
        inline int getid(char ch) { return ch-'0'; }
        inline int newnode() {
            next[qsz][0] = next[qsz][1] = fail[qsz] = ed[qsz] = 0;
            return qsz++;
        }
        
        void init() {
            qsz = 0;
            root = newnode();
        }
        
        void insert(char s[]) {
            int i, id, rt = root;
            for (i=0; s[i]; ++i) {
                id = getid(s[i]);
                if (!next[rt][id]) next[rt][id] = newnode();
                rt = next[rt][id];
            }
            ed[rt] = true;
        }
        
        void build() {
            int i, rt = root;
            queue<int> q;
            if (next[rt][0]) q.push(next[rt][0]);
            if (next[rt][1]) q.push(next[rt][1]);
            
            while (!q.empty()) {
                rt = q.front(); q.pop();
                ed[rt] |= ed[fail[rt]];
                for (i=0; i<2; ++i) {
                    if (!next[rt][i]) next[rt][i] = next[fail[rt]][i];
                    else {
                        fail[next[rt][i]] = next[fail[rt]][i];
                        q.push(next[rt][i]);
                    }
                }
            } 
        }
    
    }ac;
    
    char str[32], left[256], right[256];
    int digit[256];
    
    int dp[256][2048];
    
    ll dfs(int deep, int state, bool zero, bool lmt) {
        if (!deep) return !zero;
        if (!lmt && !zero && dp[deep][state]>=0) return dp[deep][state];
        int i, j, nex, up = lmt ? digit[deep] : 9;
        ll res = 0;
        for (i=0; i<=up; ++i) {
            if (zero && i==0) 
                res = (dfs(deep-1, 0, true, lmt && i==up) + res) % mod;
            else {
                nex = state;
                for (j=8; j; j>>=1) {
                    if (i & j) nex = ac.next[nex][1];
                    else nex = ac.next[nex][0];
                    if (ac.ed[nex]) break;
                }    
                if (ac.ed[nex]) continue;
                res = (dfs(deep-1, nex, false, lmt && i==up) + res) % mod;            
            }
    
        }
        return (lmt || zero) ? res :  dp[deep][state] = res;
    }
    
    ll cal(char s[], bool isleft) {
        int i, k, j, t, len, tt;
        tt = len = strlen(s);
        if (isleft) {
            tt--;
            while (s[tt] == '0') s[tt--] = '9';
            s[tt]-=1;        
        }
        if (tt == 0 && s[0]=='0') tt = 1;
        else tt = 0;
        for (k=0, i=tt; i<len; ++i) {
            digit[++k] = s[i] - '0';
        } 
            
        for (i=k,j=1; j<=k/2; ++j,--i) {
            t = digit[i];
            digit[i] = digit[j];
            digit[j] = t;
        }
        return dfs(k, 0, true, true); 
    }
    
    int main()
    {
    //    freopen("E:\input.txt", "r", stdin);
        int t, n, i;
        scanf("%d", &t);
        while (t--) {
            memset(dp, -1, sizeof(dp)); 
            ac.init();    
            scanf("%d", &n);
            for (i=0; i<n; ++i) {
                scanf("%s", str);
                ac.insert(str);
            }
            ac.build();
            scanf("%s%s", left, right);
        //    printf("%s %s
    ", left, right);
            printf("%lld
    ", (cal(right, false) - cal(left, true) + mod) % mod);
        }    
            
        return 0; 
    }
    View Code
    
    

    Solved
    1 / 6 O HDU 4511 小明系列故事――女友的考验 题意: 要从点1走到点n,其中有些路径不能走.问最短距离.而且每次走点都是得递增走的. 分析: 最短路,我们可以考虑一下floyd等算法,我们发现,如果除掉"有些路径不能走"这个限制条件. 那么这个题就没意思了. emmm, 那么现在的问题是,我们如何维护下一个状态可不可以走呢? 路径emmm,用AC自动机来构建我们状态的图吧. 然后从第一个点出发(注意是从第一个点出发,而不是我们平时从根节点出发). dp[state][i]表示从state状态到第i个点的最小花费. 所以我们有 dp[nex_state][j] = min(dp[nex_state][j], dp[state][i]+dist[i][j])
    #include <cmath>
    #include <queue>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    struct Point {
        double x;
        double y;
        Point () { }
        Point (int xx, int yy) : x(xx), y(yy) { }
        double dist(Point &pt) { return sqrt((x - pt.x) * (x - pt.x) + (y - pt.y) * (y - pt.y)); }
    }pt[64];
    
    double dist[64][64];
    
    class AC {
    public:
        int next[512][64];
        int fail[512];
        bool bad[512];
        int qsz, root, n;
        
        // R 1   D 0
        inline int getid (char ch) { return ch=='R'; }     
        inline int newnode() {
            int i;
            for (i=0; i<n; ++i) next[qsz][i] = 0;
            bad[qsz] = fail[qsz] = 0;
            return qsz++;
        }
        
        init(int n) {
            qsz = 0;
            this->n = n;
            root = newnode();
        }
        
        void insert(int k) {
            int i, id, rt = root;
            for (i=0; i<k; ++i) {
                scanf("%d", &id);
                id--;
                if (!next[rt][id]) next[rt][id] = newnode();
                rt = next[rt][id];
            }
            bad[rt] = true;
        }
        
        void build() {
            queue<int> q;
            int i, rt = root;
            for (i=0; i<n; ++i) 
                if (next[rt][i]) 
                    q.push(next[rt][i]);
            
            while (!q.empty()) {
                rt = q.front(); q.pop();
                bad[rt] |= bad[fail[rt]];
                for (i=0; i<n; ++i) {
                    if (!next[rt][i]) next[rt][i] = next[fail[rt]][i];
                    else {
                        fail[next[rt][i]] = next[fail[rt]][i];
                        q.push(next[rt][i]);
                    }
                }
            }
        }
        
        const double INF = (1LL << 60);
        double dp[64][512];
        void slove() {
            int i, j, k, nex, z;
            for (i=0; i<n; ++i) 
                for (j=0; j<qsz; ++j) 
                        dp[i][j] = INF;    
            dp[0][next[root][0]] = 0.0; 
            for (i=0; i<n; ++i) 
                for (z=0; z<qsz; ++z) {
                    if (bad[z]) continue;
                    for (j=i+1; j<n; ++j) {
                        nex = next[z][j]; 
                        if (bad[nex]) { continue; }
                        dp[j][nex] = min(dp[j][nex], dp[i][z] + dist[i][j]);
                    }
                }
            
            double res = INF;
            for (i=0; i<qsz; ++i) 
                if (!bad[i]) res = min(res, dp[n-1][i]);
            if (res == INF)  printf("Can not be reached!
    ");
            else printf("%.2f
    ", res);
        }
        
    }ac;
    
    int main()
    {
        int n, m, i, j, k;
        while (scanf("%d%d", &n, &m) && (n || m)) {
            ac.init(n);
            for (i=0; i<n; ++i) 
                scanf("%lf%lf", &pt[i].x, &pt[i].y);
            for (i=0; i<n; ++i) 
                for (j=0; j<n; ++j) 
                    dist[i][j] = pt[i].dist(pt[j]);
                    
            for (i=0; i<m; ++i) {
                scanf("%d", &k);
                ac.insert(k);
            }
            ac.build();
            ac.slove();
        }
        
        return 0;
    }
    View Code
    
    
    
    Solved    1 / 6    P    HDU 4057    Rescue the Rabbit
    题意:  给定n个模式串(ATGC),都有一个价值,然后问ATGC组成的长度m的串的最大价值是多少.
           注意,每个模式串的价值只能计算一次.可以重叠.
           (1 ≤ n ≤ 10),l (1 ≤ m ≤ 100) 1 <= wi <= 100
    分析:  因为不知道最优是选择几个模式串在答案里, 注意到每个模式串只能计算一次,而且只有十个.
           所以,我们可以考虑状压.
           我们这样考虑我们的dp状态, bool dp[length][state][state_k],表示这个状态可以到达.
           最后我再枚举dp[m][0...qsz][0...state_k],然后根据state_k计算答案.
           然后我们发现,内存太大了 会爆...不过length这一维是可以通过滚动数组来优化的,所以就OK.
           (前面的,有关长度的题也可以用滚动数组优化.)
    #include <queue>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    int n, m;
    char str[128];
    int val[128];
    
    class AC {
    public:
        int next[1024][4];
        int fail[1024];
        int state[1024];
        
        int root, qsz;
        
        inline int getid(char ch) {
            switch (ch) {
                case 'A': return 0;
                case 'T': return 1;
                case 'G': return 2;
                case 'C': return 3;
            }
        }
        
        inline int newnode() {
            int i;
            for (i=0; i<4; ++i) 
                next[qsz][i] = 0;
            fail[qsz] = state[qsz] = 0;
            return qsz++;
        } 
        
        void init() {
            qsz = 0;
            root = newnode();        
        }
        
        void insert(char s[], int tid) {
            int i, id, rt = 0;
            for (i=0; s[i]; ++i) {
                id = getid(s[i]);
                if (!next[rt][id]) next[rt][id] = newnode();
                rt = next[rt][id];
            }
            state[rt] |= (1 << tid);
        }
        
        inline int getval(int v) {
            int i, res = 0;
            for (i=0; i<n; ++i) 
                if (v & (1 << i)) 
                    res += val[i];        
            return res;
        }
        
        void build() {
            int rt, i;
            queue<int> q;
            rt = 0;
            for (i=0; i<4; ++i)
                if (next[rt][i]) q.push(next[rt][i]);
            while(!q.empty()) {
                rt = q.front(); q.pop();
                state[rt] |= state[fail[rt]];
                for (i=0; i<4; ++i) {
                    if (!next[rt][i]) next[rt][i] = next[fail[rt]][i];
                    else {
                        fail[next[rt][i]] = next[fail[rt]][i];
                        q.push(next[rt][i]);
                    }
                }
            }
        }
        
        int dp[2][1024][1024];
        int nex;
        int getans() {
            int i, j, k, z;
            memset(dp, 0, sizeof(dp));
            int flag = 0;
            dp[1][0][0] = 1;
            for (i=0; i<m; ++i,flag^=1) {
                memset(dp[flag], 0, sizeof(dp[flag]));
                for (z=0; z<(1<<n); ++z) {
                    for (j=0; j<qsz; ++j) {
                        if (!dp[flag^1][z][j]) continue;
                        for (k=0; k<4; ++k) {
                            nex = next[j][k];
                            dp[flag][z|state[nex]][nex] = 1;
                        }
                    }    
                }
            }
            int res = -862621363;
            for (i=0; i<(1<<n); ++i) 
                for (j=0; j<qsz; ++j) 
                    if (dp[flag^1][i][j]) 
                        res = max(res, getval(i));
            return res;
        }
    }ac;
    
    int main()
    {
        int i, j, tp;
    //    freopen("E:\input.txt", "r", stdin);
        while (~scanf("%d%d", &n, &m)) {
            ac.init();
            for (i=0; i<n; ++i) {
                scanf("%s%d", str, &tp);
                val[i] = tp;
                ac.insert(str, i);
            }
            ac.build();
            int res = ac.getans();
            if (res < 0) printf("No Rabbit after 2012!
    ");
            else printf("%d
    ", res);
        }
        
        return 0;
    } 
    View Code
    
    
    

    Solved
    1 / 3 Q HDU 4758 Walk Through Squares 题意: 输入n,m 你可以走n次R m次D. 现在给你两个模式串,问你走出这两个模式串的方案数,可以重叠. 分析: 我们走字符串以后4种状态: 没有走过字符串, 走了一个, 走了另一个, 走了两个. 所以我们有dp数组: dp[state][R][D][4]表示方案数... 然后over.
    #include <queue>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    const int mod = 1000000007;
    
    class AC {
    public:
        int next[205][2];
        int fail[205];
        int ed[205];
        int qsz, root;
        
        // R 1   D 0
        inline int getid (char ch) { return ch=='R'; }     
        inline int newnode() {
            next[qsz][0] = next[qsz][1] = ed[qsz] = fail[qsz] = 0;
            return qsz++;
        }
        
        init() {
            qsz = 0;
            root = newnode();
        }
        
        void insert(char s[], int tid) {
            int i, id, rt = 0;
            for (i=0; s[i]; ++i) {
                id = getid(s[i]);
                if (!next[rt][id]) next[rt][id] = newnode();
                rt = next[rt][id];
            }
            ed[rt] |= (1 << tid);
        }
        
        void build() {
            queue<int> q;
            int i, rt = root;
            if (next[rt][0]) q.push(next[rt][0]);
            if (next[rt][1]) q.push(next[rt][1]);
            while (!q.empty()) {
                rt = q.front(); q.pop();
                ed[rt] |= ed[fail[rt]];
                for (i=0; i<2; ++i) {
                    if (!next[rt][i]) next[rt][i] = next[fail[rt]][i];
                    else {
                        fail[next[rt][i]] = next[fail[rt]][i];
                        q.push(next[rt][i]);
                    }
                }
            }
        }
        
        // R 1   D 0
        int dp[201][101][101][4]; 
        int getans(int m, int n) {
            int R, D, i, j, k, z;
            memset(dp, 0, sizeof(dp));
            dp[0][0][0][0] = 1;
            for (R=0; R<=n; ++R) 
                for (D=0; D<=m; ++D) 
                    for (i=0; i<qsz; ++i) {
                        for (k=0; k<2; ++k) {
                            for (z=0; z<4; ++z) {
                                if (k==0 && D==m) continue;
                                if (k==1 && R==n) continue;
                                int nex = next[i][k];
                                switch (k) {
                                case 0: dp[nex][R][D+1][z|ed[nex]] = (dp[nex][R][D+1][z|ed[nex]] + dp[i][R][D][z]) % mod; break;
                                case 1: dp[nex][R+1][D][z|ed[nex]] = (dp[nex][R+1][D][z|ed[nex]] + dp[i][R][D][z]) % mod; break;
                                }
                            }
                        }                    
                    }
            int res = 0;
            for (i=0; i<qsz; ++i) 
                res = (res + dp[i][n][m][3]) % mod;
            return res;
        }
        
    }ac;
    
    int main() {
        int t, n, m, i, j;
        char str[128];
    //    freopen("E:\input.txt", "r", stdin);
        scanf("%d", &t);
        while (t--) {
            ac.init();
            scanf("%d%d", &n, &m);
            scanf("%s", str);  ac.insert(str, 0);
            scanf("%s", str);  ac.insert(str, 1);
            ac.build();
            printf("%d
    ", ac.getans(m, n));
        }    
        
        return 0;
    }
    View Code
    
    
  • 相关阅读:
    测试用例设计方法
    软件测试--常考的证书
    fillder-----接口延时返回(新方法)
    测试工作之--adb代码
    设计测试用例的常用方法
    TFS2013 设置签出独占锁
    Sql Server (MSSQLSERVER) 服务无法启动
    Sql 四大排名函数(ROW_NUMBER、RANK、DENSE_RANK、NTILE)简介
    C#使用redis学习笔记
    C#使用mybatis学习笔记
  • 原文地址:https://www.cnblogs.com/cgjh/p/9737111.html
Copyright © 2011-2022 走看看