跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

数据结构核心:KMP 算法、Trie 树与并查集详解

KMP 算法通过构建 next 数组优化字符串匹配,避免暴力回溯,将复杂度降至线性。Trie 树利用前缀共享特性,实现高效的字符串存储与检索。并查集通过树形结构与路径压缩,快速处理集合合并及连通性判断。三者均为算法竞赛与工程实践中的核心工具,配合 C++ 实现可显著提升数据处理性能。

未来可期发布于 2026/3/26更新于 2026/7/2534 浏览
数据结构核心:KMP 算法、Trie 树与并查集详解

在处理字符串匹配与集合管理问题时,暴力解法往往效率低下。本文深入解析 KMP 算法、Trie 树及并查集的核心原理与 C++ 实现。相比传统方法,这些结构能显著降低时间复杂度,是算法竞赛与工程实践中的必备工具。

KMP 算法

KMP 主要用于解决主串 S 中查找模板串 P 的问题。暴力匹配在字符不匹配时会回退主串指针,导致 O(n*m) 的时间复杂度。KMP 的核心在于利用已匹配部分的信息,避免不必要的回溯。

关键在于 next 数组(或称前缀表)。next[i] 表示以 i 结尾的子串中,最长相等前后缀的长度。当匹配失败时,根据 next 数组调整 P 串的位置,无需移动 S 串指针。很多人对 next 数组感到困惑,其实它本质上就是找最长公共前后缀。

构造 next 数组时,我们维护一个指针 j 表示当前匹配的前缀长度。若 P[i] == P[j+1],则 j 加一;否则利用 next[j] 回溯 j,直到匹配成功或 j 归零。这里要注意,代码中通常使用 1-based 索引来简化逻辑。

#include<iostream>
using namespace std;

const int N = 1e5 + 10;
char S[N], P[N];
int ne[N]; // next 数组

int main() {
    int n, m;
    cin >> n >> (P + 1) >> m >> (S + 1); // 1-based 索引

    // 求 next 数组
    for (int i = 2, j = 0; i <= n; i++) {
        while (j && P[i] != P[j + 1]) j = ne[j];
        if (P[i] == P[j + 1]) j++;
        ne[i] = j;
    }

    // 匹配过程
    for (int i = 1, j = 0; i <= m; i++) {
        while (j && S[i] != P[j + 1]) j = ne[j];
        if (S[i] == P[j + 1]) j++;
        if (j == n) {
            printf("%d ", i - n); // 输出匹配起始位置
            j = ne[j]; // 继续寻找下一个匹配
        }
    }
    return 0;
}

Trie 树

Trie 树(字典树)是一种专门用于存储字符串集合的数据结构。它通过共享公共前缀来节省空间,查询效率取决于字符串长度而非集合大小。想象一下,如果我们要存几万个单词,普通哈希表可能冲突,Trie 树就能完美解决。

每个节点代表一个字符,从根节点出发,路径即为单词。通常用二维数组 son[u][c] 存储子节点,cnt[u] 记录以该节点结尾的单词数量。插入和查询操作都是沿着字符路径走一遍即可。

#include<iostream>
using namespace std;

const int N = 1e5 + 10;
char str[N];
int son[N][26], cnt[N], idx; // idx 为当前使用的节点编号

// 插入操作
void insert(char str[]) {
    int p = 0;
    for (int i = 0; str[i]; i++) {
        int u = str[i] - 'a';
        if (!son[p][u]) son[p][u] = ++idx;
        p = son[p][u];
    }
    cnt[p]++;
}

// 查询操作
int query(char str[]) {
    int p = 0;
    for (int i = 0; str[i]; i++) {
        int u = str[i] - 'a';
        if (!son[p][u]) return 0;
        p = son[p][u];
    }
    return cnt[p];
}

int main() {
    int n;
    scanf("%d", &n);
    while (n--) {
        char op[2];
        scanf("%s%s", op, str);
        if (op[0] == 'I') insert(str);
        else printf("%d\n", query(str));
    }
    return 0;
}

并查集

并查集用于处理不相交集合的合并与查询问题。每个集合用一棵树表示,树根即为集合编号。主要支持两个操作:合并两个集合、判断两个元素是否在同一集合。遇到成对关系判断,比如朋友的朋友是不是朋友,并查集是最优解。

基础实现中,p[x] 存储 x 的父节点。为了优化性能,引入路径压缩:在 find 操作中,将沿途节点的父节点直接指向根节点。这样后续查询会非常快。

#include<iostream>
using namespace std;

const int N = 1e5 + 10;
int p[N], n, m;

int find(int x) {
    if (p[x] != x) p[x] = find(p[x]); // 路径压缩
    return p[x];
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) p[i] = i; // 初始化
    while (m--) {
        char op[2];
        int a, b;
        scanf("%s%d%d", op, &a, &b);
        if (op[0] == 'M') p[find(a)] = find(b); // 合并
        else puts(find(a) == find(b) ? "Yes" : "No"); // 判断
    }
    return 0;
}

若需统计集合大小,可增加 size 数组。合并时将小集合的 size 累加到大集合上。实际运行中,记得初始化 size 为 1。

#include<iostream>
using namespace std;

const int N = 1e5 + 10;
int p[N], sizes[N], n, m;

int find(int x) {
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        p[i] = i;
        sizes[i] = 1; // 初始大小为 1
    }
    while (m--) {
        char op[5];
        int a, b;
        scanf("%s", op);
        if (op[0] == 'C') {
            scanf("%d%d", &a, &b);
            int rootA = find(a), rootB = find(b);
            if (rootA != rootB) {
                sizes[rootB] += sizes[rootA];
                p[rootA] = rootB;
            }
        } else if (op[1] == '1') {
            scanf("%d%d", &a, &b);
            puts(find(a) == find(b) ? "Yes" : "No");
        } else {
            scanf("%d", &a);
            printf("%d\n", sizes[find(a)]);
        }
    }
    return 0;
}

目录

  1. KMP 算法
  2. Trie 树
  3. 并查集
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 使用 llama.cpp 快速部署本地大模型教程
  • Llama-Factory 在金融舆情分析中的实际应用案例
  • 基于 browser-tools-mcp 的 MCP 插件配置实战
  • Cursor 配置指南:让 AI 辅助 Java 高效开发
  • FastAPI:Python 高性能 Web 框架核心特性
  • 开放代理产品功能介绍
  • 基于 ComfyUI 工作流的 Stable Diffusion 服装替换实战
  • Transformer 模型核心原理与从零实现详解
  • Rust 控制流实战:条件、循环与模式匹配
  • 鸿蒙 ArkUI 基础 Grid 网格布局详解
  • Python SQLAlchemy ORM 数据库操作指南
  • AI 产品经理核心职责、技能体系与实战策略指南
  • AI 大模型重塑物流业:行业实践与架构演进
  • ComfyUI:AI 绘画与图像生成的高效工作流指南
  • Undertow 高危漏洞 CVE-2025-12543 应急响应与企业防护策略
  • Mac M 芯片 OpenClaw 环境搭建与运行指南
  • Trae 集成 Figma MCP 实现前端代码自动生成
  • Unity 5.2.0 引擎核心功能、图形渲染与性能优化指南
  • AWPortrait-Z LoRA 强度与推理步数协同调优指南
  • 大模型检索增强生成(RAG)技术综述

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online