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

原文地址:https://www.cnblogs.com/cgjh/p/9737111.html