首页 > 编程语言 >KMP算法

KMP算法

时间:2024-03-30 21:44:55浏览次数:27  
标签:target 后缀 pattern next 算法 KMP 字符串 size

一. 概述

要解决的问题:字符串匹配问题。

  • 目标串target:"aabaabaafa"
  • 模式串pattern:"aabaaf"

传统算法:

双层for循环遍历目标串target和模式串pattern,判断pattern在target第一次出现的位置。

时间复杂度为:\(O(pattern.size()*target.size())\)=\(O(m*n)\)

KMP算法核心思路:

在对目标进行匹配时,如果\(pattern[j] != target[i]\):

  • 之前的做法:\(j = 0; i=i+1\)
  • 改进后:i不变,我们寻找j前面字符串的最长公共前后缀(假设长度为m),那么令j=m,随后再比较pattern[j]和target[i]。

kmp的核心在于避免对模式串的相同前后缀进行多次比较。

最长公共前后缀:

指的是字符串中相等且最长的前后缀串的长度。例如:

  • a:0。
  • aa:1
  • aab:0
  • aaba:1
  • aabaa:2
  • aabaaf:0

假如匹配到aabaaf的 f 时发现不匹配,那么就看 f 前面的字符串,aabaa的最长公共前后缀为2,因此下次从下标2,也就是 b 开始比较。

二. 求next数组

通过上面的描述,我们发现首先要计算出pattern中每个字符前面的字符串的长公共前后缀,也就是俗称的next数组。

Eg:以上述的pattern为例,它的next数组如下:

a a b a a f
0 1 0 1 2 0

核心思路:

求字符串的最长公共前后缀,实际上也是一个字符串匹配的问题,只不过这一次不是匹配两个字符串,而是匹配字符串的前缀和后缀,难点在于前缀和后缀长度是动态变化的,我们用 j 指向前缀字符串的末尾,用 i 指向后缀字符串的末尾(同时也通过 i 来遍历pattern串):

  1. 如果\([i]==[j]\),那么\(next[i]=j\)。
  2. 如果\([i]!=[j]\),那么表示匹配失败,这时采用kmp算法,查看 j 前面的字符串的最长公共前后缀,也就是 next[j-1]的值。令\(j=next[j-1]\),然后再次比较 [i] 和 [j],递归1、2步。

可以看出,这实际上是一个递归的过程,我们为了使用kmp算法,需要求pattern的next数组,而在求pattern的next数组过程中,我们又对字符串[0,...,j]和[i-j,...,i]使用了kmp算法。

代码:

// 求next数组
void get_next(vector<int>& next, string& s){
    // 1. 初始化
    int j = 0;
    next[0] = j;
    for(int i = 1; i < s.size(); i++){
        // 2. 判断前后缀不相等的情况
        // 注意:此处是while而不是if。要保证j>0,避免越界操作
        while(j > 0 && s[i] != s[j]){
            // 递归使用kmp算法
            j = next[j - 1];        
        }
        // 3. 判断前后缀相等的情况
        if(s[i] == s[j]){
            j++; 
        }
        // 4. 赋值 
        next[i] = j;
    }
}

三.代码实现

当我们求出pattern的next数组后,kmp的核心工作实际上已经完成。剩下的代码就是简单的遍历判断了:

// 求next数组
void get_next(vector<int>& next, string& s){
    // 1. 初始化
    int j = 0;
    next[0] = j;
    for(int i = 1; i < s.size(); i++){
        // 2. 判断前后缀不相等的情况
        // 注意:此处是while而不是if。要保证j>0,避免越界操作
        while(j > 0 && s[i] != s[j]){
            // 递归使用kmp算法
            j = next[j - 1];        
        }
        // 3. 判断前后缀相等的情况
        if(s[i] == s[j]){
            j++; 
        }
        // 4. 赋值 
        next[i] = j;
    }
}
int my_strstr(string& target, string& pattern){
    if(pattern.size() == 0){
        return 0;
    }
    // 求模式串的next数组
    vector<int> next(pattern.size(), 0);
    get_next(next, pattern);
    int j = 0;      // j指向模式串
    // 遍历目标串
    for(int i = 0; i < target.size(); i++){
        // 注意这里还是while
        while(j > 0 && target[i] != pattern[j]){
            j = next[j - 1];
        }    
        // 如果等于
        if(target[i] == pattern[j]){
            j++;
        }
        if(j == pattern.size()){
            return i - j + 1;
        }
    }
    return -1;
}

标签:target,后缀,pattern,next,算法,KMP,字符串,size
From: https://www.cnblogs.com/beasts777/p/18106071

相关文章

  • 一维差分算法
    目录背景:一维插分应用场景一维差分法原理解释背景:在刷javaA组蓝桥本真题时,碰到了一个用二维差分方法的解题思路,意识到自己差分不熟悉,就想先学习一下一维差分。一维插分应用场景题目给出一个一维数组,并多次修改某一区间的值。例如:一个数组长度为8,初始均为0,输入数......
  • 毕业设计:基于深度学习的电影推荐算法 -- 以豆瓣为例 大数据
    目录前言设计思路一、课题背景与意义二、算法理论原理2.1GRU网络模型2.2语言模型2.3推荐算法三、检测的实现3.1数据集3.2实验环境搭建3.3实验及结果分析最后前言    ......
  • CHC5223数据结构和算法
    CHC5223数据结构和算法2023-2024第2学期课业1价值40%的课程个人工作学习成果学生将能够理解:1.1数据结构1.2数据结构的应用1.3面向对象编程概念1.4程序测试方法学生将掌握以下方面的技能:2.1数据抽象2.2数据结构的使用2.3使用高级面向对象语言进行更高级的编程2.4程序测试......
  • 算法模板 v1.10.5.20240330
    算法模板v1.1.1.20240115:之前历史版本已不可寻,创建第一份算法模板。v1.2.1.20240116:删除“编译”-“手动开栈”;删除“编译”-“手动开O优化”;修改“编译”-“CF模板”;删除“读写”;删除“图论”-“欧拉图”-“混合图”;删除“图论”-“可达性统计”;删除“数据类型”-“高精类”。......
  • 《算法笔记》系列----质数的判断(埃氏筛法)
    目录一、朴素算法二、埃氏筛法1、与朴素算法对比2、算法介绍   3、例题即代码实现一、朴素算法 从素数的定义中可以知道,一个整数n要被判断为素数,需要判断n是否能被2.3.n-1中的一个整除。只2,3..n-1都不能整除n,n才能判定为素数,而只要有一个能整除n的数出现,n就......
  • 算法学习——LeetCode力扣动态规划篇1
    算法学习——LeetCode力扣动态规划篇1509.斐波那契数509.斐波那契数-力扣(LeetCode)描述斐波那契数(通常用F(n)表示)形成的序列称为斐波那契数列。该数列由0和1开始,后面的每一项数字都是前面两项数字的和。也就是:F(0)=0,F(1)=1F(n)=F(n-1)+F(n-2......
  • 滑动窗口算法(Sliding Window Algorithm)
    滑动窗口的核心就是,右指针给窗口扩容,直至抵达扩容限制条件或抵达边界;左指针则是给窗口缩容,以释放限制条件的约束,保证窗口继续向边界移动。需求讲解给定一个字符串str,请找出其中不含有重复字符的最长子串的长度。publicstaticintlengthOfLongestSubstring(Stringstr){......
  • 有关链表算法题的一些思考
    1.针对链表的特性(1)双指针的方法:因为不管是删除还是添加元素,都要涉及指定位置元素的上一个元素,因此需要设置前后两个指针来实现操作,同时针对题目特殊性也可能会有三指针的情况,如LeetCode82的去重,第一个指针作为删除操作的前一个指针,而后两个指针则用来查取重复范围/**......
  • 四数之和算法讲解
    题目给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a],nums[b],nums[c],nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):0<=a,b,c,d <na、b、c 和 d 互不相同nums[a]+num......
  • 图解《程序员面试常见的十大算法》及代码实现
    关注我,持续分享逻辑思维&管理思维;可提供大厂面试辅导、及定制化求职/在职/管理/架构辅导;有意找工作的同学,请参考博主的原创:《面试官心得--面试前应该如何准备》,《面试官心得--面试时如何进行自我介绍》, 《做好面试准备,迎接2024金三银四》。推荐热榜内容:《C#实例:SQL如何添加......