KMP字符串匹配算法中next数组的求取

之前笔者通过代码随想录学习了kmp算法,但是一直对于kmp算法next的数组求解过程并不是特别清楚,这几天我复习KMP算法时对next数组的求解有了一些自己的理解。于是想写一篇博客来发表一下自己的看法,希望能帮助大家更好的理解KMP算法。

(如果之前对于kmp算法不太了解的同学可以先点击下面的链接学习kmp算法的思想)

代码随想录kmp算法讲解链接

 如果现在你已经了解了KMP算法的思想

现在我重点阐述如何求解next数组

代码如下所示

void dealnext(std::vector<int>& next,std::string &target) {
			
			next[0] = 0;
			int j = 0;//j为指向前缀末尾位置
			for (int i = 1; i < target.size(); i++) {
				if (target[i] == target[j]) {//如果前缀最后一个字符和后缀最后一个字符相等
					j++;
					next[i] = j;
					continue;
				}
				while (j > 0 && target[i] != target[j]) {
					j = next[j - 1];

				}
				if (target[i] == target[j]) {
					j++;
				}
				next[i] = j;
			}
		}

求解next是一个动态规划的过程

初始值

next[i]代表的字符字串(target.substr(target.begin(),target.begin()+i))前后缀最长相等长度

动态规划的初始化即为next[0]=0;(target[0]的最长相等前后缀长度为0)

我们默认i为指向后缀末尾的位置  j为指向前缀末尾的位置

用i进行循环,i从1开始(next[0]已经初始化了)

j初始化为0,表示为next[0]的前后缀最长相等长度为0;

循环去给next[i]赋值

我重点讲解 循环体内的动态规划思路:
 j如果不为0;
 那么是说明next[i-1]的不为0;
 next[i-1]的值为j;
 那么即[0,i]的字符字串的最长相等前后缀和为j;
 那么target[j]即为第j+1个字符
 如果target[j]==tagret[i];
 那么对于next[i]就等于next[i-1]+1(即为j+1);
 j自增是为了表示进入下一个循环(i++)后next[i-1]=j;
 如果target[j]!=target[i]
 我们已经得到了next[i-1]的值为j(假设此时j>0)
 即以i-1结尾的字串前后缀相等的字串最大长度为j;
那么这个最长的相等前后缀的前缀字串即为target.substr(target.begin(),tagrget.bebgin()+j)
我们已经得到这字符串子的最长前后缀相等长度了,(i的值肯定要大于next[i]的)大小为next[j-1];    所以我们就再判断str[next[j-1]]是否等于str[i]
如果相等next[i]=next[j-1]+1;
如果不等 ,那么继续向前跳,
跳的终止条件即为x的大小为0;
x的大小为0了说明next[i-1]没有相等前缀和,此时是让target[0]与taget[i]进行匹配,                         果不匹配,那么next[i]=0;i++,继续向后判断next[i]

总结

我们动态规划求next数组时,如果str[i]=target[j]那么很好理解,j++,next[i]++;

如果str[i]!=target[j]那么我们既要利用前面的求取的next数组,复用了kmp算法的字符串匹配思想,然后找到最长相等前后缀长度

KMP算法笔者的模拟实现:

namespace zjb {
	class KMP {
		int Kmp(std::string str,std::string target){
			if (target == "") {
				return 0;
			}
			std::vector<int>next(target.size(),0);
			dealnext(next, target);
			int index = 0;
			for (int i = 0; i < str.size(); i++) {
				if (index >= target.size()) {
					return i - index;
				}
				if (str[i] == target[index]) {
					index++;
					continue;
				}
				while(str[i]!=target[index]&&index!=0) {
					index = next[index - 1];
				}
				if (str[i]==target[index]) {
					index++;
				}
			}
            if(index==target.size()){
                return str.size()-index;
            }
			return -1;
		}
		void dealnext(std::vector<int>& next,std::string &target) {
			//next[i]代表的是以i为下标的前后缀最长相等长度
			next[0] = 0;
			int j = 0;//j为指向前缀末尾位置
			for (int i = 1; i < target.size(); i++) {
				if (target[i] == target[j]) {//如果前缀最后一个字符和后缀最后一个字符相等
					j++;
					next[i] = j;
					continue;
				}
				while (j > 0 && target[i] != target[j]) {
					j = next[j - 1];
				}
				if (target[i] == target[j]) {
					j++;
				}
				next[i] = j;
			}
		}
	};
}

猜你喜欢

转载自blog.csdn.net/m0_56910081/article/details/126453522
今日推荐