字符串匹配KMP算法(笔记)

next[i]

next[i]记录字符串str[0,i]相等的最长前后缀的前缀的最后一位下标

例如有一字符串 str="abab"
字符串前缀有 “a”,“ab”,"aba"
字符串后缀又 “b”,“ab”,"bab"
那么str的相等的最长前后缀分为别 “ab”,“ab”,前缀"ab"最后一个字符’b’的下标为1,所以字符串str的next[3]=1。

next[i]计算需要排除自身,即前、后缀不能选自己。

  • 变量j的含义
    我们引入变量j的主要目的是在计算next[i]时,使用next[i-1]的成果,找个变量j=next[i-1],那么next[i]的值为j+1,即迭代公式
    next[ i ] = next[ i - 1 ] +1
    但是这个迭代公式使用有一个条件
    str[ i ] == str[ j + 1 ]
    也就是说变量 j 的作用是在计算next [ i ] 的时候,存储str [ 0, i - 1 ]的最长相等前后缀的最后一个字符的下边,即 next[ i - 1 ],即j = next [ i - 1 ];
    在计算next [2 ]的时候,为什么j初始化赋值为-1呢?
    根据上面说的 j = next[ i - 1 ]= next[ 1 ],对于str[ 0, 1 ] = “ab”,不难计算next[ 1 ] = -1;
    注意,对于任何字符串都有next[ 0 ] = -1
    得出一个结论,
    str[ i ] == str[ j + 1 ]时,next[ i ] = j+1;

  • str[ i ] != str[ j + 1 ]时应该如何处理?
    这时,只能把j往前移,最优移动步骤为一直赋值
    j = next[ j ],直到str[ i ] == str [ j + 1 ]成立
    一直退到j=-1,而j+1=0,即字符串str 的头部,无法再退了,意味着只能重头开始匹配。
    此时如果j = -1, str[ i ] == str[ j + 1 ]仍然不成立的话,
    只能宣布next[ i ] = -1,即str[ 0, i ] 的最长相等前后缀为空

next数组快速计算代码实现

vector<int>getNext(const string& str) {
	vector<int>next(str.size());
	next[0] = -1;
	for (int i = 1, j = -1; i < str.size(); i++) {
		while (j != -1 && str[i] != str[j + 1]) {
			//如果不能匹配,只能退j
			j = next[j];
		}
		if (str[i] == str[j + 1]) {
			//如果成功匹配了一个字符,则next[i] = j+1
			//否则next[i]=-1
			j++;
		}
		next[i] = j;
	}return next;
}

KMP算法

例如有strOne = “ababaabc”, strTwo = “abaab”,判断strOne是否包含子串strTwo.
变量i的作用是指示strOne字符串下标,变量j的作用是表示已经连续匹配成功的strTwo下标

j后移的含义:strTwo成功连续匹配到一个字符,j后移

j往前退,j = next[ j ]

j到达字符串strTwo的尾端,即匹配成功

如果j一直退还不满足strTwo[ j + 1]==strOne[ i ],也就是j退到strTwo的起始位置都不满足条件,此时只能后移i,期待后面能匹配成功。

KMP算法的代码实现

//判断字符串strOne是否包含子串strTwo
bool kmp(const string& strOne, const string& strTwo) {
	//先计算字符串strTwo的next数组
	vector<int>strTwoNext = getNext(strTwo);
	for (int i = 0, j = -1; i < strOne.size(); i++) {
		while (j != -1 && strOne[i] != strTwo[j + 1]) {
			//如果不匹配只能一直退j
			j = strTwoNext[j];
		}
		if (strOne[i] == strTwo[j + 1]) {
			//匹配成功了一个字符,则j后移
			j++;
		}
		if (j == strTwo.size() - 1) {
			//j已经到达了strTwo的尾端,说明匹配已经完成
			return true;
		}
	}return false;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值