数据结构核心:KMP 算法、Trie 树与并查集详解
字符串匹配和集合管理是算法竞赛及工程中的高频场景。今天我们来深入聊聊三个经典数据结构:KMP 算法、Trie 树以及并查集。我会结合 C++ 代码,把原理和实现细节掰开揉碎讲清楚。
KMP 算法
暴力匹配的瓶颈
处理字符串匹配问题时,最直观的想法是暴力枚举。假设主串为 S,模式串为 P,暴力做法就是拿 P 去跟 S 的每一个位置比对。一旦遇到不匹配,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;
}
}
}
这种写法在数据量大时很容易超时,因为每次失败都要回溯。我们需要利用模式串本身的性质来优化。
核心原理与 next 数组
KMP 的核心在于:当匹配失败时,我们不需要把 P 从头开始,而是根据已经匹配的部分,直接跳到下一个可能的位置。这个'跳多少'的信息,就存在 next 数组里。
next[i] 的含义是:以 i 结尾的子串中,最长相等前后缀的长度。比如 next[i] = j,意味着 P[1...j] 和 P[i-j+1...i] 是相等的。
求 next 数组的过程其实也是一个匹配过程。我们假设 j 是当前匹配长度,如果 P[i] == P[j+1],说明可以延长匹配;如果不等,就利用 next[j] 回退,直到找到可匹配的前缀或 j=0。
// 计算 next 数组(ne 即 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;
}
匹配过程实战
有了 next 数组,匹配主串时就非常高效了。主串指针 i 永远向前,模式串指针 j 根据 next 数组回退。
for(int i = 1, j = ; i <= m; i++) {
(j && S[i] != P[j + ]) j = ne[j];
(S[i] == P[j + ]) j++;
(j == n) {
(, i - n);
j = ne[j];
}
}


