【第5期】算法精选-你应该知道的KMP算法

简介: 【第5期】算法精选-你应该知道的KMP算法

KMP要解决的问题


先说一下这个算法出现的背景,也就是解决什么问题。每个算法和框架都有它出现的原因和要解决的问题,很多时候你不会一个技术,并不是你笨,而是你没有找到它的使用场景或者是没搞明白它要解决的问题而已。所以,了解它要解决的问题,是学习的重中之重。

首先看一个例子:

字符串str="abcabcabcde",字符串match="abcabcde"

如果str包含子串match,返回match在str中的起始位置。

这个问题其实可以使用暴力来破解,如下图:

image.png


image.png

image.png

从str[i]开始(i初始等于0),每次只要遇到了match和str不匹配的情况,str回退到str[i+1],再继续str[i+1]和match[0]依次对比,长此以往,直到match完全匹配出来或者str遍历完毕为止,这样做的时间复杂度是O(M*N),M是str的长度,N是match的长度。

那么有没有种方法可以不这么笨拙的解决字符串匹配的问题呢?


KMP算法就是解决match和str在匹配过程中不停的做回退的问题的。简单一句话,KMP算法是做字符串高效匹配的算法


KMP算法是如何做的


说KMP的解法之前,先看看一个聪明的人类是如何解决这个问题的:


image.png

这样做的原因是没必要一次性退回从match[0]和str[1]开始比较,因为可以看出来绿色框内的match[0,1,2]和str[3,4,5]是重合的,重合的这部分完全可以复用,没有必要再从match[0]和str[1]开始重复比较;

具体的过程可以模拟抽象成下图:


image.png

所以问题转化成了:

每次str[j]和match[j]不相同时,在match[0...j-1]这段字符串中,以match[j-1]结尾的后缀子串(不能包含match[0])和以match[0]开头的前缀子串(不能包含match[j-1])的最大匹配长度是多少?只要求出这个最大匹配长度,就能在str[j]和match[j]不相同时,知道把match滑动到什么位置了!

KMP算法的重点就是维护一个数组,保存match中每个字符在不匹配时,match应该滑动到什么位置,这个数组起名叫next。


如何构造next数组


那么如何构造next数组呢?

首先对于match[0]来说,它前面没有字符,所以next[0]规定为-1;对于match[1]来说,因为next数组规定计算的时候子串后缀不能包含第一个字符,所以next[1]=0;

说完特殊情况,再说说常规情况,比如现在假设match[i]是A字符,match[i-1]是B字符,可以通过next[i-1]得到B字符前的字符串最长前缀与后缀的匹配区域,现在假设L为最长前缀子串,K为最长后缀子串,C为最长前缀子串之后的一位字符,现在只需要比较C和B即可

image.png

  1. 如果字符B等于字符C,那么A字符之前的字符串的最长前缀与后缀匹配区域就可以确定了,最长匹配前缀子串为L+C,最长匹配后缀子串为K+B,next[i] = next[i-1]+1;
  2. 如果字符B不等于字符C,就看C字符之前的前缀后缀匹配问题。假设C的位置是match[cn],那么可以通过next[cn]得到字符C前的字符串的最长匹配前缀是M,最长匹配后缀是N,再假设M后面一位字符是D,又因为L=K,所以同等位置的p=N;接下来就是比较一下B和D是否相同,如果B=D,那么A字符之前的字符串最长匹配前缀子串是M+D,最长匹配后缀子串是P+B,如果不相同的话,照着之前说的思路继续往前跳,直到跳到match[0]


image.png

代码如下


/**
 * @param {string} haystack
 * @param {string} needle
 * @return {number}
 */
var strStr = function(str, match) {
    if(str === null || match === null || match.length > str.length)
        return -1;
    if(str === "")
        return 0;
    let index1 = 0;
    let index2 = 0;
    let nextArr = getNext(match);
    console.log(nextArr);
    while(index1 < str.length && index2 < match.length) {
        if(str[index1] === match[index2]) {
            index1++;
            index2++;
        } else if(nextArr[index2] === -1) {
            index1++;   
        } else {
            index2 = nextArr[index2];
        }
    }
    return index2 === match.length ? index1 - index2 : -1;
};
var getNext = function(match) {
    let next = [-1, 0];
    let cn = 0;
    let index = 2;
    while(index < match.length) {
        if(match[index-1] === match[cn]) {
            cn++;
            next[index] = cn;
            index++;
        } 
        else if(cn > 0) {
            cn = next[cn];
        } 
        else {
            next[index] = 0;
            index++;
        }
    }
    return next;
}



相关文章
|
5月前
|
算法
数据结构中的KMP算法及其改进算法
KMP算法通过引入部分匹配表,有效避免了重复计算,从而将字符串匹配的时间复杂度降低到O(m+n)。通过进一步优化next数组,KMP算法的效率得到了进一步提升。对于大规模字符串匹配问题,KMP算法及其改进算法提供了高效的解决方案,是计算机科学领域的经典算法之一。
82 3
|
1月前
|
算法
第四章 KMP算法理论基础
第四章 KMP算法理论基础
18 0
|
1月前
|
算法
KMP算法
KMP算法
27 0
|
3月前
|
算法 C++
A : DS串应用–KMP算法
这篇文章提供了KMP算法的C++实现,包括计算模式串的next数组和在主串中查找模式串位置的函数,用于演示KMP算法的基本应用。
|
4月前
|
数据采集 算法 JavaScript
揭开JavaScript字符串搜索的秘密:indexOf、includes与KMP算法
JavaScript字符串搜索涵盖`indexOf`、`includes`及KMP算法。`indexOf`返回子字符串位置,`includes`检查是否包含子字符串。KMP是高效的搜索算法,尤其适合长模式匹配。示例展示了如何在数据采集(如网页爬虫)中使用这些方法,结合代理IP进行安全搜索。代码示例中,搜索百度新闻结果并检测是否含有特定字符串。学习这些技术能提升编程效率和性能。
118 1
揭开JavaScript字符串搜索的秘密:indexOf、includes与KMP算法
|
3月前
|
算法
KMP算法
KMP算法
27 0
|
4月前
|
算法 Java
KMP算法详解及其在字符串匹配中的应用
KMP算法详解及其在字符串匹配中的应用
|
5月前
|
人工智能 算法 BI
一篇文章讲明白KMP算法(俗称看毛片算法)
一篇文章讲明白KMP算法(俗称看毛片算法)
56 0
|
5月前
|
算法 Java
Java数据结构与算法:字符串匹配算法之KMP算法
Java数据结构与算法:字符串匹配算法之KMP算法
|
6月前
|
算法 搜索推荐 程序员
第六十三练 字符串匹配 - KMP算法
第六十三练 字符串匹配 - KMP算法
48 2