KMP算法

KMP算法

KMP算法

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。

传统字符串比较

最简单也最直观的字符串匹配算法是暴力搜索(Brute-Force Search),也称为朴素字符串搜索(Naive String Search)。该算法的基本思想是从主字符串的每个位置开始,逐个字符地比较模式字符串和主字符串的子串,如果当前字符匹配则比较下一个字符,如果不匹配则回到主字符串的下一个位置继续比较,直到找到匹配或遍历完整个主字符串。时间复杂度为O(mn),其中m是模式字符串的长度,n是主字符串的长度,空间复杂度为O(1)。

KMP算法优势

KMP算法的核心在于解决暴力求解时主串的指针回退造成的效率低下的问题,所以KMP算法的优势在于用于和模式串对比的指向主串的指针不会回退,这也是在匹配过程中,KMP算法匹配的时间复杂度为 O(n+m)的原因,其中n为主串的长度,m为模式串的长度(因为指向主串的指针不回退,所以无论最终是否找到与模式串相匹配的子串,指向主串的指针只会遍历一次主串)。

KMP算法原理

1.利用当前子字符串,寻找到字串当前位置与初始最大重复数值。方便于退回操作,不需要每次退回到初始位置进行重新操作。
2.为了寻找到这种,我们需要找到一个数组,用来比对后表示回退到什么地方。

next函数实现原理

  • 对于一个模式串,我们求取next数组就是寻找到当前位置与模式串初始位置中最大重复位置。
  • 如模式串abacabab,next求取方法如下。
  • 初始位置0,a初始位置,没有重复的字母。
  • 初始位置1,字母b,与前面无重复字符串,需要重初始位置开始重新比对。
  • 初始位置2,字母a,与前方字母a重复,若是下次对比直接可从字符串b处开始对比。
  • 初始位置3,字母c,与前面无重复字符串,需要重初始位置开始重新比对。
  • 初始位置4,字母a,与前方字母a重复,若是下次对比直接可从字符串b处开始对比。
  • 初始位置5,字母b,与前方字母ab重复,若是下次对比直接可从字符串a处开始对比。
  • 初始位置6,字母a,与前方字母aba重复,若是下次对比直接可从字符串c处开始对比。
  • 初始位置7,字母b,与前方字母ab重复,若是下次对比直接可从字符串a处开始对比。

next函数实现算法

  • 判断当前字符串i位置是与位置j+1处是否相等。
  • 如果相等,进行j++,然后将next[i]赋于j值。
  • 若是不相等,则进行回退,将next[j]值赋值给j,表示j处最大相似处,直到位置i与j+1处字符串相同位置。或者回退到字符串初始位置。

next实现代码

void getNext(int* next, const string& str){
        int j = -1;
        next[0] = j;
	for(int i = 0; i < str.size(); i++){
		while(j >= 0  && str[i] != str[j+1]){
			j = next[j];
		}
		if(str[i] == str[j+1]){
			j++;
		}
		next[i] = j;
	}
}

题目

给你两个字符串haystack和needle,请你在haystack字符串中找出needle字符串的第一个匹配项的下标(下标从0开始)。如果needle不是haystack的一部分,则返回-1 。

示例

image

代码

class Solution {
public:
    void getNext(int* next, const string& str){
        int j = -1;
        next[0] = -1;
        for(int i = 1; i < str.size(); i++){
            while(j>=0 && str[i] != str[j+1]){
                j = next[j];
            }
            if(str[i] == str[j+1]){
                j++;
            }
            next[i] = j;
        }
    }
    int strStr(string haystack, string needle) {
        if(needle.size() == 0){
            return 0;
        }
        int next[needle.size()];
        getNext(next, needle);
        int j = -1;
        for(int i = 0; i < haystack.size(); i++){
            while(haystack[i] != needle[j+1] && j >= 0 ){
                j = next[j];
            }
            if(haystack[i] == needle[j+1]){
                j++;
            }
            if(j == (needle.size()-1)){
                return (i-needle.size()+1);
            }
        }
        for(int i = 0; i < needle.size(); i++){
            cout << next[i] << " ";
        }
        return -1;
    }
};

题目

给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。

示例

image

代码

```cpp
class Solution {
public:
    void getNext(int* next, const string & s){
        int j = 0;
        next[0] = 0;
        for(int i = 1; i < s.size(); i++){
            while(j > 0 && s[i] != s[j]){
                j = next[j - 1];
            }
            if(s[i] == s[j]){
                j++;
            }
            next[i] = j;
        }
    }
    bool repeatedSubstringPattern(string s) {
        int sLen = s.size();
        int next[sLen];
        getNext(next, s);
        if(next[sLen-1] != 0 && sLen % (sLen - next[sLen-1]) == 0){
            return true;
        }
        return false;
    }
};
posted @ 2025-12-10 21:49  heyuikn  阅读(29)  评论(0)    收藏  举报