ACM模板整理

本文深入探讨了ACM竞赛中常用的字典树(Trie)数据结构,详细介绍了其原理、实现方法及其在字符串搜索、前缀匹配等问题上的应用。通过实例解析,帮助读者理解并掌握如何在算法竞赛中有效利用Trie提高解决方案的效率。
摘要由CSDN通过智能技术生成
字符串处理

字典树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:pattern
struct 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;
    }
};


1 图论 3 1.1 术语 3 1.2 独立集、覆盖集、支配集之间关系 3 1.3 DFS 4 1.3.1 割顶 6 1.3.2 桥 7 1.3.3 强连通分量 7 1.4 最小点基 7 1.5 拓扑排序 7 1.6 欧拉路 8 1.7 哈密顿路(正确?) 9 1.8 Bellman-ford 9 1.9 差分约束系统(用bellman-ford解) 10 1.10 dag最短路径 10 1.11 二分图匹配 11 1.11.1 匈牙利算法 11 1.11.2 KM算法 12 1.12 网络流 15 1.12.1 最大流 15 1.12.2 上下界的网络的最大流 17 1.12.3 上下界的网络的最小流 17 1.12.4 最小费用最大流 18 1.12.5 上下界的网络的最小费用最小流 21 2 数论 21 2.1 最大公约数gcd 21 2.2 最小公倍数lcm 22 2.3 快速幂取模B^LmodP(O(logb)) 22 2.4 Fermat小定理 22 2.5 Rabin-Miller伪素数测试 22 2.6 Pollard-rho 22 2.7 扩展欧几里德算法extended-gcd 24 2.8 欧拉定理 24 2.9 线性同余方程ax≡b(mod n) 24 2.10 中国剩余定理 25 2.11 Discrete Logging(BL == N (mod P)) 26 2.12 N!最后一个不为0的数字 27 2.13 2^14以内的素数 27 3 数据结构 31 3.1 堆(最小堆) 31 3.1.1 删除最小值元素: 31 3.1.2 插入元素和向上调整: 32 3.1.3 堆的建立 32 3.2 并查集 32 3.3 树状数组 33 3.3.1 LOWBIT 33 3.3.2 修改a[p] 33 3.3.3 前缀和A[1]+…+A[p] 34 3.3.4 一个二维树状数组的程序 34 3.4 线段树 35 3.5 字符串 38 3.5.1 字符串哈希 38 3.5.2 KMP算法 40 4 计算几何 41 4.1 直线交点 41 4.2 判断线段相交 41 4.3 三点外接圆圆心 42 4.4 判断点在多边形内 43 4.5 两圆交面积 43 4.6 最小包围圆 44 4.7 经纬度坐标 46 4.8 凸包 46 5 Problem 48 5.1 RMQ-LCA 48 5.1.1 Range Minimum Query(RMQ) 49 5.1.2 Lowest Common Ancestor (LCA) 53 5.1.3 Reduction from LCA to RMQ 56 5.1.4 From RMQ to LCA 57 5.1.5 An<O(N), O(1)> algorithm for the restricted RMQ 60 5.1.6 An AC programme 61 5.2 最长公共子序列LCS 64 5.3 最长上升子序列/最长不下降子序列(LIS) 65 5.3.1 O(n^2) 65 5.3.2 O(nlogn) 66 5.4 Joseph问题 67 5.5 0/1背包问题 68 6 组合数学相关 69 6.1 The Number of the Same BST 69 6.2 排列生成 71 6.3 逆序 72 6.3.1 归并排序求逆序 72 7 数值分析 72 7.1 二分法 72 7.2 迭代法(x=f(x)) 73 7.3 牛顿迭代 74 7.4 数值积分 74 7.5 高斯消元 75 8 其它 77
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值