数据结构中的KMP算法及其改进算法

简介: KMP算法通过引入部分匹配表,有效避免了重复计算,从而将字符串匹配的时间复杂度降低到O(m+n)。通过进一步优化next数组,KMP算法的效率得到了进一步提升。对于大规模字符串匹配问题,KMP算法及其改进算法提供了高效的解决方案,是计算机科学领域的经典算法之一。

数据结构中的KMP算法及其改进算法

在计算机科学中,字符串匹配是一个基本且重要的问题。经典的暴力匹配算法虽然简单,但在最坏情况下的时间复杂度为O(mn),其中m是模式串的长度,n是文本串的长度。为了提高匹配效率,Knuth-Morris-Pratt(KMP)算法应运而生,其时间复杂度为O(m+n),显著提升了匹配速度。本文将介绍KMP算法的基本原理及其改进算法。

KMP算法的基本原理

KMP算法的核心思想是利用部分匹配表(也称为next数组)来避免不必要的重复匹配。在进行字符串匹配时,KMP算法通过分析已经匹配的部分,决定接下来从哪里开始匹配,从而跳过一些已经确定不会匹配的字符。

步骤一:构建next数组

next数组记录了每个位置之前的部分匹配信息。具体来说,对于模式串P,next数组中的每个元素next[i]表示在位置i之前的模式串的最长相同前后缀的长度。

构建next数组的过程如下:

  1. 初始化:设定next[0] = -1,表示空字符串的前缀没有匹配。
  2. 迭代构建:使用双指针方法,一个指向当前字符,一个指向前缀的结束位置,逐步计算每个位置的next值。
void computeNextArray(const string &P, vector<int> &next) {
   
    int m = P.size();
    int j = 0;  // 前缀末尾指针
    int k = -1; // 前缀开始指针
    next[0] = -1;
    while (j < m - 1) {
   
        if (k == -1 || P[j] == P[k]) {
   
            j++;
            k++;
            next[j] = k;
        } else {
   
            k = next[k];
        }
    }
}

步骤二:进行字符串匹配

利用next数组进行匹配时,避免了暴力算法中的重复检查。在匹配过程中,遇到不匹配字符时,根据next数组跳转到适当的位置继续匹配。

int KMP(const string &T, const string &P) {
   
    int n = T.size();
    int m = P.size();
    vector<int> next(m);
    computeNextArray(P, next);

    int i = 0; // 文本串指针
    int j = 0; // 模式串指针
    while (i < n) {
   
        if (j == -1 || T[i] == P[j]) {
   
            i++;
            j++;
        } else {
   
            j = next[j];
        }
        if (j == m) {
   
            return i - j; // 匹配成功,返回起始位置
        }
    }
    return -1; // 匹配失败
}

KMP算法的改进

KMP算法尽管已经非常高效,但在构建next数组时仍有改进空间。原始的next数组中有部分重复计算,优化这些计算可以进一步提升效率。

改进的next数组(优化next数组)

改进后的next数组用next'表示,在构建过程中避免了多次回溯,通过调整指针逻辑,直接跳过不必要的匹配。

void computeNextArrayOptimized(const string &P, vector<int> &next) {
   
    int m = P.size();
    int j = 0;
    int k = -1;
    next[0] = -1;
    while (j < m - 1) {
   
        if (k == -1 || P[j] == P[k]) {
   
            j++;
            k++;
            if (P[j] != P[k]) {
   
                next[j] = k;
            } else {
   
                next[j] = next[k];
            }
        } else {
   
            k = next[k];
        }
    }
}

使用优化后的next数组进行匹配的主过程不变,但由于构建next数组的效率提高,总体性能会有所提升。

总结

KMP算法通过引入部分匹配表,有效避免了重复计算,从而将字符串匹配的时间复杂度降低到O(m+n)。通过进一步优化next数组,KMP算法的效率得到了进一步提升。对于大规模字符串匹配问题,KMP算法及其改进算法提供了高效的解决方案,是计算机科学领域的经典算法之一。

相关文章
|
1天前
|
算法 Python
数据结构算法--4堆排序
堆排序过程概述:建立大根堆,将堆顶最大元素移出并替换为末尾元素,调整保持堆性质,重复此过程直至堆为空,实现排序。时间复杂度为O(nlogn)。Python中可用heapq模块进行堆操作。
|
2天前
|
算法 Java 机器人
Java数据结构与算法:并发数据结构ConcurrentHashMap
Java数据结构与算法:并发数据结构ConcurrentHashMap
|
2天前
|
机器学习/深度学习 算法 搜索推荐
数据结构算法--2 冒泡排序,选择排序,插入排序
**基础排序算法包括冒泡排序、选择排序和插入排序。冒泡排序通过相邻元素比较交换,逐步将最大值“冒”到末尾,平均时间复杂度为O(n^2)。选择排序每次找到剩余部分的最小值与未排序部分的第一个元素交换,同样具有O(n^2)的时间复杂度。插入排序则类似玩牌,将新元素插入到已排序部分的正确位置,也是O(n^2)复杂度。这些算法适用于小规模或部分有序的数据。**
|
2天前
|
机器学习/深度学习 算法 索引
数据结构算法--1 顺序查找二分查找
**顺序查找时间复杂度为O(n)**,适合无序列表,可以通过`enumerate`或直接遍历索引来实现。**二分查找时间复杂度为O(logn)**,适用于有序列表,利用Python中`left`、`right`指针和`mid`点不断缩小搜索范围。效率上二分查找更优。
|
1天前
|
算法 网络协议 Java
我的Java数据结构和算法
我的Java数据结构和算法
7 0
|
1天前
|
算法 搜索推荐
数据结构算法--6 希尔排序和计数排序
**希尔排序**是插入排序的改进版,通过分组插入来提高效率。它逐步减少元素间的间隔(增量序列),每次对每个间隔内的元素进行插入排序,最终增量为1时进行最后一次直接插入排序,实现整体接近有序到完全有序的过程。例如,对数组`5, 7, 4, 6, 3, 1, 2, 9, 8`,先以间隔`d=4`排序,然后`d=2`,最后`d=1`,完成排序。计数排序则适用于0到100的数值,通过统计每个数出现次数,创建对应计数数组,再根据计数重建有序数组,时间复杂度为`O(n)`。
|
2天前
|
存储 算法 Java
Java数据结构与算法:线性数据结构之链表
Java数据结构与算法:线性数据结构之链表
|
2天前
|
算法 Java 编译器
Java数据结构与算法:线性数据结构之栈
Java数据结构与算法:线性数据结构之栈
|
8天前
数据结构——栈和队列
数据结构——栈和队列
10 1
|
2天前
|
C++
【洛谷 P1044】[NOIP2003 普及组] 栈 题解(递归+记忆化搜索)
**NOIP2003普及组栈问题**:给定操作数序列1到n,仅允许push(进栈)和pop(出栈)操作。目标是计算所有可能的输出序列总数。输入包含一个整数n(1≤n≤18)。示例输入3,输出5。当队列空时返回1,栈空则只能入栈,栈非空时可入栈或出栈。AC C++代码利用记忆化搜索求解。
5 1

热门文章

最新文章