ACM模板整理

来源:互联网 发布:qq for mac历史版本 编辑:程序博客网 时间:2024/05/21 08:36
字符串处理

字典树Trie

/* 字典树Trie */struct Trie {    // 最大节点数:模式串个数 * 最大串长度    static const int MAX_NODE = 10000 * 50 + 50;    static const int CHAR_SET = 26; // 字符集的大小,也是Trie树中每个节点所连的最大边数    static const int BASE     = 'a';    int n;                          // trie大小    int id[BASE+CHAR_SET+1];        // 字符->int    int tag[MAX_NODE];              // 标记,根据需要调整    int trie[MAX_NODE][CHAR_SET];    void init() {        for (int i=0;i<CHAR_SET;++i)            id[BASE+i] = i;        n = 1;        memset(trie[0], -1, sizeof(trie[0]));        memset(tag, 0, sizeof(tag));    }    // 添加一个单词    void add(const char *s) {        int p = 0;        while (*s) {            int i = id[*s];            if (trie[p][i] == -1) {                memset(trie[n], -1, sizeof(trie[n]));                trie[p][i] = n++;            }            ++s;            p = trie[p][i];            tag[p]++;        }    }    // 在字典中查询以s为前缀的单词个数    int Search(char * s) {        int p = 0;        while (*s) {            int i = id[*s];            if (trie[p][i] == -1) break;            ++s;            p = trie[p][i];        }        if (*s)            return 0;        else            return tag[p];    }};
AC自动机

/* AC自动机 解决多模式串匹配问题 * 边:值为一个字符 * 节点:值L(v)={从根节点到v的路径上所有边的值的序列} * 根节点为空,代表初始状态。每个模式串pi都可以找到L(v) = pi。 * fail函数: 如果L(q) 是 L(v) 的一个最长后缀 则 fail(v)=q * 时间复杂度: O(n+m+z) n:模式串总长度 m:文本长 z:文本中模式串出现次数 */struct ACAutomation {    // 最大节点数:模式串个数 * 最大串长度    static const int MAX_NODE = 10000 * 50 + 50;    static const int CHAR_SET = 26;    static const int BASE     = 'a';    int n;                          // trie大小    int id[BASE+CHAR_SET+1];        // 字符->int    int tag[MAX_NODE];              // 标记,根据需要调整    int last[MAX_NODE];             // 后缀链接,链接到上一个单词节点    int fail[MAX_NODE];    int trie[MAX_NODE][CHAR_SET];    void init() {        for (int i=0;i<CHAR_SET;++i)            id[BASE+i] = i;        n = 1;        memset(trie[0], -1, sizeof(trie[0]));        memset(tag, 0, sizeof(tag));        memset(last, 0, sizeof(last));    }    void add(const char *s) {        int p = 0;        while (*s) {            int i = id[*s];            if (trie[p][i] == -1) {                memset(trie[n], -1, sizeof(trie[n]));                trie[p][i] = n++;            }            ++s;            p = trie[p][i];        }        //tag[p] = 1;        tag[p]++;    }    void build() {        queue<int> Q;        fail[0] = 0;        int u, v;        for (int i=0;i<CHAR_SET;++i) {            u = trie[0][i];            if (u != -1) {                fail[u] = 0;                Q.push(u);            }            else                trie[0][i] = 0;        }        while (!Q.empty()) {            int fr = Q.front();Q.pop();            for (int i=0;i<CHAR_SET;++i) {                int &u = trie[fr][i];    // 注意这里使用了一个引用                if ( u != -1 ) {                    Q.push(u);                    fail[u] = trie[fail[fr]][i];                    last[u] = tag[fail[u]] ? fail[u] : last[fail[u]];                }                else                    u = trie[fail[fr]][i];            }        }    }    //    // 已经把所有指向“不存在”的边指向了合适的位置    // 每次在自动机上做一次转移即可    // 例子:hdu2222 统计出现过的关键词,在找到一个关键词后将清除tag标记    int search(const char * T, int len) {        int p = 0;        int ret = 0;        for (int i=0;i<len;++i) {            //cout << "on " << T[i] << endl;            if (T[i] < 'a' || T[i] > 'z') {                p = 0;                continue;            }            int c = id[T[i]];            p = trie[p][c];            int v = last[p];            // 检查当前节点是否是单词            if (tag[p]) {ret += tag[p];tag[p] = 0;}            // 沿着后缀链接走, 检查是否为单词            while (v) {                if (tag[v]) {                    ret += tag[v];                    tag[v] = 0;                }                v = last[v];            }            //cout << "match on node: " << p << " cnt:  " << ret << endl;        }        return ret;    }};
KMP
// KMP <span style="font-size:18px;">单模式串查找</span>// T:Text P:patternstruct KMP{    // 构造失配函数    void getFail(const char *P, int *F)    {        int len = strlen(P);        F[0] = 0;        F[1] = 0;        REP(i, 1, len-1) {            int j = F[i];            while(j & P[j] != P[i])                j = F[j];            F[i+1] = P[i] == P[j] ? j+1 : 0;        }    }    // 查找模式串在文本中的出现次数    int find(const char * T, const char *P, int *f) {        int n = strlen(T), m = strlen(P);        getFail(P, f);        int j = 0;        int num = 0;        REP(i, 0, n-1) {            while(j && P[j] != T[i])                j = f[j];            if (P[j] == T[i])                ++j;    // 当完成了一个匹配后 状态是肯定要转移一次的    // 因为字符串的最后有 '\0' 所以不用显示写出来(在下一次调用失配函数的时候顺带完成)    // 但如果是整数列 则要显示地调用一次失配函数 j = f[j]            if (j == m)                ++num;        }        return num;    }};


0 0