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

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

KMP 算法通过 next 数组优化字符串匹配,避免主串指针回退。Trie 树利用字典结构高效存储检索字符串。并查集管理集合合并与查询,支持路径压缩。文章提供三种算法原理及 C++ 代码模板,涵盖 next 数组构建、Trie 插入查询、并查集合并统计等核心实现,适合初学者掌握数据结构基础。

狂少发布于 2026/3/16更新于 2026/9/466 浏览
数据结构:KMP 算法、Trie 树与并查集详解

KMP 算法

KMP 算法用于处理字符串匹配问题。给定主串 S[] 和模板串 P[],通常使用下标 1 开始遍历。

暴力匹配

暴力方法在遇到不匹配字符时,P 串从头开始重新匹配 S 串的下一个位置。当串很长时容易超时。

for(int i = 1; i <= m; i ++){
    bool flag = true;
    for(int j = 1; j <= n; j++){
        if(S[i+j-1] != P[j]){
            flag = false;
            break;
        }
    }
}

原理

利用 P 串本身相同的前缀和后缀性质。当匹配失败时,无需逐个回退,直接跳到特定位置继续匹配。

next 数组

next[i] 表示以 i 为终点的后缀和从 1 开始的前缀相等的最长长度。 若 next[i] = j,则 p[1...j] = p[i-j+1...i]。

构造逻辑:假设 i-1 位置之前的最长前缀后缀长度为 j,若 S[i] == P[j+1],则 j++,ne[i] = j;否则利用 ne[j] 回溯查找。

// 求 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;
}

匹配过程

指针 i 指向主串,j 指向模式串。若 S[i] != P[j+1],j 回退到 ne[j];若相等,j++。当 j==n 时匹配成功。

#include<iostream>
using namespace std;
const int N = 1e4 + 10;
const int M = 1e5 + 10;
char S[M], P[N], ne[N];
int n, m;

int main(){
    cin >> n >> (P + 1) >> m >> (S + 1);
    // 求 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 树(字典树)用于快速存储和查找字符串集合。

核心思想:从根节点开始,将单词字母一一存入,最后一个字母做标记。cnt[p] 记录以当前点结尾的单词数量。

#include<iostream>
using namespace std;
const int N = 1e5 + 10;
char str[N];
int son[N][26], cnt[N], 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] 存储父节点。

  • 判断树根:if(p[x] == x)
  • 求集合编号:while(p[x] != x) x = p[x]
  • 合并集合:p[find(x)] = find(y)

优化:路径压缩。

#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{
            if(find(a) == find(b)) puts("Yes");
            else puts("No");
        }
    }
    return 0;
}

若需统计集合大小,可添加 size 数组。合并时 sizes[find(b)] += sizes[find(a)]。

#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int p[N], n, m, sizes[N];

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;
    while(m --){
        char op[5];
        int a, b;
        scanf("%s", op);
        if(op[0] == 'C') {
            scanf("%d%d", &a, &b);
            if(find(a) == find(b)) continue;
            sizes[find(b)] += sizes[find(a)];
            p[find(a)] = find(b);
        } else if(op[1] == '1'){
            scanf("%d%d", &a, &b);
            if(find(a) == find(b)) puts("Yes");
            else puts("No");
        } else {
            scanf("%d", &a);
            printf("%d\n", sizes[find(a)]);
        }
    }
    return 0;
}

目录

  1. KMP 算法
  2. 暴力匹配
  3. 原理
  4. next 数组
  5. 匹配过程
  6. Trie 树
  7. 并查集

更多推荐文章

查看全部
  • Clawdbot 对接企业微信机器人单向推送全流程指南
  • Linux 开源邮件服务及 iRedMail 部署实操指南
  • Java Web 开发:JSON 基础、@Test 测试及 Cookie Session 处理
  • 低代码 AI 平台 Coze 核心功能与使用指南
  • 大模型落地路线图研究报告(2024 年)
  • JavaScript 浮点数精度丢失与金额分摊解决方案
  • 大模型基本技术:架构、训练与推理优化详解
  • Debian 系统 libwebkit2gtk-4.1-0 安装后无法加载问题排查
  • Copilot助力AI原生应用:提升开发效率的5种方法
  • WebODM完全指南:零基础掌握开源无人机地图制作
  • Java 核心语法与并发编程实战指南
  • Python 开源 AI 模型引入与测试全流程实战
  • OpenSpec 规范驱动 AI 协作:深入解析开发流程变革
  • Python 流程控制详解:条件语句、循环语句与实战
  • 基于 Whisper-large-v3 的短视频配音识别与字幕生成
  • 双指针算法实战:移动零与复写零详解
  • SQL 语言中 DDL、DML 与 DCL 的区别解析
  • C# 后端导出 Excel 并实现前端直接下载方案
  • Open-Lovable 网页克隆工具及 cpolar 远程访问配置
  • 2026 年 AI 大数据、大模型、AIGC 与云计算就业趋势分析

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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