KMP算法,next数组详解(c++)

目录

1.BF算法原理

2.KMP算法原理

3.next数组原理


1.BF算法原理

BF算法本质是上是双重for循环。只是用一个循环来实现的通过j=i-j+1;的方式将主串的下标从"头"开始遍历。

代码如下:

int BF(const char* S, const char* T) 
{
    if (S == NULL || T == NULL) 
    {
        return -1;
    }
    int lenstr = strlen(S);
    int lensub = strlen(T);
    int i = 0;
    int j = 0;
    while (i < lenstr && j < lensub) 
    {
        if (S[i] == T[j]) 
        {
            i++;
            j++;
        }
        else 
        {
            i = i - j + 1;//利用这个公式将i回退到对应的位置
            j = 0;		 
        }
    }
    if (j >= lensub)   
    { 
        return i - j;
    }
    else
        return -1;

}                 

2.KMP算法原理

假设dadstr是主字符串,sonstr是子字符串,这两者是要比较的子字符串是否在主字符串中的字符串。如果当dadstr[i]!=sonstr[j]时,按照BF算法i时要回退到暴力for循环第几次开始对应的位置的,但是如果sonstr字符串是局部头尾对称的呢,比如sonstr是:abcabf我们可以知道当f与主字符串不相等时,f之前的字符串是头尾对称的,此时我们还需要将i回退吗,不需要因为既然能到f位置那么f之前的位置和主字符串肯定相等,并且abcab是对称的,我们就没必要i回退了,我们只需要将j回退到c位置再开始就是下一个比较的位置了。

换一种理解方式,假设有主字符串aweq和子字符串weq。我们可以这样去比较,如果if dadstr[i]==sonstr[j];i++,j++; if dadstr[i]!=sonstr[j] && dadstr[i]!=sonstr[0] ;j=0,i++; else j=0;这一串代码的意思就是i去遍历主字符串,j去遍历子字符串,如果 i位置的主字符串和j位置的字字符串相等那么就继续往后面遍历,如果不相等i无需要回退,我直接将j回退到头重新比较。这样的比较也是符合逻辑的,但是如果是这样的两对字符串呢? ->主字符串acbabcabcef和子字符串abcabcef我们发现我们刚刚的那个代码无法得到正确答案,为什么呢?因为如果按照刚刚那个算法j直接回退到0,但是我们发现在这种情况中,我们直接回退到0位置是错了,直接回退到0位置会导致少了一部分字符串比较而这一个字符串刚刚好是我们的答案,所以结果错了。所以我们要在此算法的基础上去讨论将j回退到什么位置才行。这就是next数组出现的理由,因为对称所以next数组就决定了j回退到正确的位置。

代码如下:

int strStr(string dadstr, string sonstr)
{
	int* next = new int[sonstr.size()];
	memset(next,0, sonstr.size()*4);
	GetNext(next, sonstr);

	int len1 = dadstr.size(); //计算出主串的长度
	int len2 = sonstr.size();   //计算出子串的长度


	if (len1 == 0 && len2 != 0) return -1;
	if (len1 == 0 && len2 == 0) return 0;
	if (len1 != 0 && len2 == 0) return 0;

	int j = 0;//主串的下标
	int i = 0;//子串的下标
    
	while (i < len1 && j < len2)
	{
		如果主串和子串的字符相同或者j回退到了-1位置,我们都要把i和j进行++
		if ((j == -1) ||dadstr[i] == sonstr[j])
		{
			i++;
			j++;
		}
		else
		{
			j = next[j];//主串和子串的字符不匹配,j需要依靠next数组进行回退操作
		}
	}

	if (j >= len2) return i - j;//子串遍历完了
	else return -1;//子串没有遍历完了
}

3.next数组原理

接上面所说的KMP算法原理中next数组的由来,这里讨论怎么得出next数组。next数组中next[i]是指的到i位置时i之前的字符串中尾部是否与前面具有对称性,且是第几个对称。所以我们只需要判断sonstr[i]是否满足对称即可。

例如:abcabf 对应的next数组中的元素应该是 0 0 0 1 2 0 。

i=0时,"a"字符串中无头尾对称(一个字符不算)所以next[0]=0;

i=1时,"ab"字符串中也无头尾对称,所以next[1]=0;

i=2时,"abc"字符串中也无头尾对称,所以next[2]=0;

i=3时,"abca"字符串中尾部'a'与头部'a'对称且是第一个对称,所以next[3]=1;

i=4时,"abcab"字符串中尾部"ab"与头部"ab"对称且'b'是第二个对称,所以next[4]=2;

i=5时,"abcabf"字符串中无头尾对称,所以next[5]=0;

那么根据next数组的组成原理,我们可以写出这样的代码。

//方法一:
void Getnext(int* next, const string& t) {
	int i = 0;
	next[0] = -1;
	int j = -1;
	while(i < t.length) {
		if(j == -1||t.ch[i] == t.ch[j]) {
			i++;
			j++;
			next[i] = j;
		} else {
			j = next[j];
		}
	}
}

这就是next数组的代码,但是根据next数组的定义"next数组中next[i]是指的到i位置时的i前面字符串中尾部是否与前面具有对称性,且是第几个对称。"next[i]是到i位置时i前面的字符串的特性,这里还可以写成到i位置时包括i字符串的特性。此时创建next数组代码还可以写成这样:

//方法二:
void GetNext(int* next, const string& s)
{
	int size = 1;
	for(;size < s.size();size++)
	{
		if (next[size-1] == 0)
		{
			if (s[size] == s[0])
				next[size] = 1;
			else
				next[size] = 0;
		}
		else
		{
			if (s[size] == s[next[size - 1]])
				next[size] = next[size - 1]+1;
			else
				next[size] = 0;
		}
	}
	next[0] = -1;
}

同理不同的next数组所写的KMP算法不一样。这里以教材书上为准也就是方法一的代码。

如有错误欢迎指出。

相关推荐
A懿轩A1 小时前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
机器视觉知识推荐、就业指导1 小时前
C++设计模式:享元模式 (附文字处理系统中的字符对象案例)
c++
半盏茶香1 小时前
在21世纪的我用C语言探寻世界本质 ——编译和链接(编译环境和运行环境)
c语言·开发语言·c++·算法
Evand J2 小时前
LOS/NLOS环境建模与三维TOA定位,MATLAB仿真程序,可自定义锚点数量和轨迹点长度
开发语言·matlab
LucianaiB2 小时前
探索CSDN博客数据:使用Python爬虫技术
开发语言·爬虫·python
Ronin3052 小时前
11.vector的介绍及模拟实现
开发语言·c++
✿ ༺ ོIT技术༻2 小时前
C++11:新特性&右值引用&移动语义
linux·数据结构·c++
字节高级特工2 小时前
【C++】深入剖析默认成员函数3:拷贝构造函数
c语言·c++
计算机学长大白3 小时前
C中设计不允许继承的类的实现方法是什么?
c语言·开发语言
PieroPc4 小时前
Python 写的 智慧记 进销存 辅助 程序 导入导出 excel 可打印
开发语言·python·excel