03_KMP算法

(13条消息) 从头到尾彻底理解KMP(2014年8月22日版)_v_JULY_v的博客-CSDN博客_从头到尾彻底理解kmp

代码随想录 (programmercarl.com)

(13条消息) 数据结构KMP算法配图详解(超详细)_哈顿之光的博客-CSDN博客_数据结构kmp算法

从该博客学习到的知识,便于以后复习使用,记录下来,我觉得KMP算法的核心思想就是利用对称的思想。

一、什么是KMP算法

二、next[ ] 数组表示什么

三、前缀表和next[  ]之间的关系,为什么要用前缀表

四、最长相等前后缀

五、next[ ] 数组构造

六、KMP算法的优化

七、力扣28题


一、什么是是KMP算法

        KMP算法用于解决在暴力匹配算法,当字符串不匹配时,模式串要重头进行重新匹配的问题,KMP算法的重要思想是:当出现字符串不匹配时,可以记录之前一部分匹配的内容,利用这些信息避免重头匹配问题。

二、next[ ] 数组表示什么

        next[ ] 数组表示当前字符之前的字符串中,有最大长度相同的前缀和后缀,例如:next[j]=k代表j位置之前的字符串中,有最大长度为k的相同前缀和后缀。

三、前缀表和next[ ]之间的关系,为什么要用前缀表

        next[ ] 数可以表示一个前缀表,前缀表:前缀表是用来回退的,它记录了模式串与主串不匹配时,模式串应该从哪个位置重新开始匹配。next数组可以是前缀表向右移动一位,也可以是前缀表值-1。

        用前缀表的原因是:因为前缀表找到了最长相等的前缀和后缀,匹配失败的位置是后缀子串的后面,那么我们找到与其相同的前缀的后面从新匹配就可以了,所以前缀表具有告诉我们当前位置匹配失败,跳到之前已经匹配过的地方的能力。

四、最长相等前后缀(例如模式串:aabbaca)

(1)前缀:不包含最后一个字符且以首字符开头的连续子串。

        前缀有:a aa aab aabb aabba aabbac

(2)后缀:不包含开头字符且以最后一个字符结尾的连续子串。

        后缀有: a ca aca baca bbaca abbaca

(3)最长相等前后缀:前缀子串和后缀子串相等时候的最大长度。

模式串aabbaca
next[ ]-1010010

所以举个例子:a的最长相等前后缀为0,aa的最长相等的前后缀为1,aaa的最长相等的前后缀长度为2。

五、next数组的构造

(1)对前缀表不做操作处理

        ①初始化

定义两个指针i和j,j指向前缀末尾位置,i指向后缀末尾位置。

然后还要对next数组进行初始化赋值,如下:

int i = 0;
next[0]=i //要始终记住next就是前缀表的长度

        ②处理前后缀不相同的情况

        ③处理前后缀相同的情况

        ④确定前缀长度

前缀表不移动的情况

void getNextval(string s,int next[]){
        next[0] = 0;
        int i = 0;
        for(int j = 1;j < s.size(); j++){//注意从1开始
            while(i > 0 && s[i] != s[j]) {//前后缀不相同了
//如果s[i]与s[j]不相同,就要找i前一个元素在next数组的值就是回退
                i = next[i - 1];//向前回退
            }
            if(s[i] == s[j]) { //找到相同的前后缀
                i++;
            }
            next[j] = i;//将i(前缀长度)赋给next【j】
        }
    }

(2)将前缀表右移一位

typedef struct
{	
	char data[MaxSize];
	int length;			//串长
} SqString;
//SqString 是串的数据结构
//typedef重命名结构体变量,可以用SqString t定义一个结构体。
void GetNext(SqString t,int next[])		//由模式串t求出next值
{
	int j,k;
	j=0;k=-1;
	next[0]=-1;//第一个字符前无字符串,给值-1
    int len=t.size();
	while (j<len-1) 
	//因为next数组中j最大为t.length-1,而每一步next数组赋值都是在j++之后
	//所以最后一次经过while循环时j为t.length-2
	{	
		if (k==-1 || t.data[j]==t.data[k]) 	//k为-1或比较的字符相等时
		{	
			j++;k++;
			next[j]=k;
			//对应字符匹配情况下,s与t指向同步后移
			//通过字符串"aaaaab"求next数组过程想一下这一步的意义
			//printf("(1) j=%d,k=%d,next[%d]=%d\n",j,k,j,k);
       	}
       	else
		{
			k=next[k];
			**//我们现在知道next[k]的值代表的是下标为k的字符前面的字符串最长相等前后缀的长度
             //前缀回溯
			//也表示该处字符不匹配时应该回溯到的字符的下标
			//这个值给k后又进行while循环判断,此时t.data[k]即指最长相等前缀后一个字符**
			//printf("(2) k=%d\n",k);
		}
	}
}
void GetNext(char* p,int next[])
{
	int pLen = strlen(p);
	next[0] = -1;
	int k = -1;
	int j = 0;
	while (j < pLen - 1)
	{
		//p[k]表示前缀,p[j]表示后缀
		if (k == -1 || p[j] == p[k]) 
		{
			++k;
			++j;
			next[j] = k;
		}
		else 
		{
			k = next[k];
		}
	}
}

六、KMP的优化

//优化过后的next 数组求法
void GetNextval(char* p, int next[])
{
	int pLen = strlen(p);
	next[0] = -1;
	int k = -1;//k表示前缀末尾的位置
	int j = 0;//j表示后缀末尾位置
	while (j < pLen - 1)
	{
		//p[k]表示前缀,p[j]表示后缀  
		if (k == -1 || p[j] == p[k])
		{
			++j;
			++k;
			//较之前next数组求法,改动在下面4行
			if (p[j] != p[k])
				next[j] = k;   //之前只有这一行
			else
				//因为不能出现p[j] = p[ next[j ]],所以当出现时需要继续递归,k = next[k] = next[next[k]]
				next[j] = next[k];
		}
		else
		{
			k = next[k];
		}
	}
}

七、力扣28

实现 strStr() 函数。

给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回  -1 。

由于容器的size()返回值类型是一个unsigned int无符号数故而需要转化成有符号数,一种方法是int() 另一种方法是static_cast<int>() 转化。

class Solution {
public:
    void getNext(string needle,int next[]){
        int i=-1;
        int j=0;
        next[0]=-1;
        while(j<needle.size()-1){
            if(i==-1||needle[i]==needle[j]){
                j++;
                i++;
                if(needle[j]!=needle[i]) next[j]=i;
                else next[j]=next[i];
            }else i=next[i];
        }
    }
    int strStr(string haystack, string needle) {
        //KMP算法实现串的模式匹配
        if(needle.size()==0) return 0;
        int next[needle.size()];
        getNext(needle,next);
        int i=0,j=0;
        while(i<static_cast<int>(haystack.size())&&j<static_cast<int>(needle.size())){
            if(j==-1||haystack[i]==needle[j]){
                ++i;
                ++j;
            }else{
                j=next[j];
            }
        }
        if(j>=needle.size()) return i-needle.size();
        else return-1;
        //普通方法实现串的模式匹配问题
        // int i=0,j=0;
        // if(haystack.size()<needle.size()) return -1;
        // while(i<haystack.size()&&j<needle.size()){
        //     if(haystack[i]==needle[j]){
        //             i++;
        //             j++;
        //     }else{
        //         i=i-j+1;
        //         j=0;
        //     }
        // }
        // return j==needle.size()?i-j:-1;

    }
};

力扣459

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

class Solution {
public:
    void getNextval(string s,int next[]){
        next[0] = 0;
        int i = 0;
        for(int j = 1;j < s.size(); j++){
            while(i > 0 && s[i] != s[j]) {
                i = next[i - 1];
            }
            if(s[i] == s[j]) {
                i++;
            }
            next[j] = i;
        }
    }
    bool repeatedSubstringPattern(string s) {
        //s=s+'a';
        int slen=s.size();
        if(slen==1) return false;
        int next[slen];
        getNextval(s,next);
        if(next[slen-1]!=0&&slen%(slen-next[slen-1])==0) return true;
        else return false;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
KMP算法是一种高效的字符串匹配法,它通过预处理模式串,构建next数组,来实现在匹配过程中跳过些不必要的比较。下面是KMP算法的C++代码实现[^1]: ```cpp #include <iostream> using namespace std; const int MAXLEN = 255; typedef struct { char ch[MAXLEN]; int length; } SString; void GetNext(SString T, int next[]) { int i = 1, j = 0; next = 0; while (i < T.length) { if (j == 0 || T.ch[i] == T.ch[j]) { ++i; ++j; next[i] = j; } else { j = next[j]; } } } int Index_KMP(SString S, SString T, int next[]) { int i = 1, j = 1; while (i <= S.length && j <= T.length) { if (j == 0 || S.ch[i] == T.ch[j]) { ++i; ++j; } else { j = next[j]; } } if (j > T.length) { return i - T.length; } return 0; } int main() { SString S, T; S.length = 10; T.length = 3; strcpy(S.ch, "ababcabcac"); strcpy(T.ch, "abc"); int next[MAXLEN]; GetNext(T, next); int pos = Index_KMP(S, T, next); if (pos != 0) { cout << "匹配成功,位置为:" << pos << endl; } else { cout << "匹配失败" << endl; } return 0; } ``` 以上是KMP算法的C++代码实现。在代码中,我们首先定义了一个结构体SString来表示字符串,包含一个字符数组ch和一个长度length。然后,我们实现了GetNext函数来计算模式串T的next数组,用于在匹配过程中跳过不必要的比较。接着,我们实现了Index_KMP函数来进行KMP算法的匹配过程。最后,在main函数中,我们定义了两个字符串S和T,并调用GetNext函数计算T的next数组,然后调用Index_KMP函数进行匹配,并输出匹配结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值