KMP算法详解

KMP算法

  • KMP算法用于串的模式匹配
  • 设主串为 text,模板串为 str
  • 若匹配成功返回该模板串首次出现在主串的下标,若失败则返回 -1

暴力解法

  • i_t 为主串比较的下标
  • i_s 为模板串比较的下标
  • 若某次该位置匹配成功,主串和模板串的比较位置都置为下一位;若 i_s到达边界,则匹配成功,退出循环
  • 若匹配失败,模板串比较位置退回到起始位置,即 i_s = 0 ,所以 i_s 也是已经匹配成功的长度;主串比较位置改为上一轮开始比较位置的下一位置,相当于主串整体前移一位,即为 i_t = i_t - i_s + 1 ;若 i_t 越界,则匹配失败,退出循环
int violentSolution(string text, string str) {
	//return text.find(str);	//用于测试
	
	int i_t = 0;	//主串比较下标
	int i_s = 0;	//模板串比较下标

	while (i_t < text.length() && i_s < str.length()) {
		if (text[i_t] == str[i_s]) {
			i_t++;
			i_s++;
		}
		else {
			i_t = i_t - i_s + 1;	//主串整体前移一位
			i_s = 0;				//模板串比较位置重新置为开头位置
		}		
	}
	//错误的,若在最后匹配成功了,例如:text = "123abc",str = "abc",匹配成功时两个下标都会分别等于串长度
	//return i_t == text.length() ? -1 : i_t - i_s;
	//应该以 模板串下标为判断标准
    return i_s = str.length() ? i_t - i_s : -1;
}
  • 注意最后的返回,应以模板串下标来判断

KMP解法

前缀、后缀和部分匹配值

  • 前缀:除去末位字符,串中所有 头部子串,必须包含首位字符

  • 后缀:除去首位字符,串中所有 尾部子串,必须包含末位字符

  • 部分匹配值:串的前缀和后缀的 最长相等前后缀长度

计算模板串的next数组

对于模板串 str : ababaaababaa,其next数组为:

下标01234567891011
字符ababaaababaa
部分匹配值-100123112345

next 数组值为: -1 0 0 1 2 3 1 1 2 3 4 5

在这里插入图片描述

  • 规定 :
next[0] = -1;
next[1] = 0;
  • next[j] 就是 以 0~j -1 组成的字符串的 部分匹配值

    例如:next[5] = 3str[5] = a,对于 0~4组成的串ababa,其前缀有: a、ab、aba、abab;其后缀有:baba、aba、ba、a;最长相等前后缀为 aba ,即部分匹配值为 3 。

  • 部分匹配值 next[j] 在 KMP 算法中有何作用?

    决定了与主串 text 匹配时,若该位 j str[j] != text[i] 时,证明了模板串这次从 头到 j-1 位都是匹配成功的,但下次匹配时,不需从头开始匹配,此时模板串 strj - next[j]j - 1 的字符(后缀)已经是和 主串 text0next[j] - 1 的字符(前缀)匹配成功了,只需让 text[i]str[next[j]] 再次比较,循环这个过程,可以减少多次重复比较。

  • 如图:

计算过程如下:

  • i:计算部分匹配值的位置;cur:与str[i]比较的位置
  • 比较 i-1 位置的字符 与 next[i-1]即为 cur 位置的字符是否相同
  • 若相同,next[i] 为 next[i-1]的值+1;同时i++,cur也++
  • 若不同,cur = next[cur],再次比较,一直到 cur = 0时,此时str[i-1]与str[0]比较,若相同,则next[i] = 1;若不同为0
void getNext(string str,int *next) {
	if (str.length() == 0) {
		return;
	}
	if (str.length() == 1) {
		next[0] = -1;
	}
	next[0] = -1;
	next[1] = 0;
	int i = 2;		// 计算部分匹配值的位置
	int cur = 0;	// 与str[i]比较的位置

    // 对应三个分支
	while (i < str.length()) {
		if (str[i - 1] == str[cur]) {
			next[i++] = ++cur;
		}
		else if(cur>0) {
			cur = next[cur];
		}
		else {	//cur=0,且比较不相同的情况
			next[i++] = 0;
		}
	}
}

KMP

  • 如果比较相同,则都++,相当于主串和模板串的比较位置都后移一位
  • 如果不同,主串位置不动,模板串比较位置置为next[i_s];
  • 一直到模板串首字符与 text[i_t]比较,若相同则继续1操作,若不同则将主串比较位置后移一位,下一轮比较
int kmp(string text, string str) {
	if (text.length() < 1 || str.length() < 1) {
		return -1;
	}
	int i_t = 0;	//主串该比较下标
	int i_s = 0;	//模板串比较下标
	int* next = new int[str.length()];
	getNext(str, next);
	// 1. 如果比较相同,则都++,相当于主串和模板串的比较位置都后移一位
	// 2. 如果不同,主串位置不动,模板串比较位置置为next[i_s];
	// 一直到模板串首字符与 text[i_t]比较,若相同则继续1操作,若不同则将主串比较位置后移一位,下一轮比较
	while (i_t < text.length() && i_s < str.length()) {
		if (text[i_t] == str[i_s]) {
			i_t++;
			i_s++;
		}
		else if(next[i_s] == -1) {
			i_t++;
		}
		else {
			i_s = next[i_s];
		}
	}
	delete[] next;
	return i_s = str.length() ? i_t - i_s : -1;
}
  • 同暴力解法,返回应以模板串的下标来判断,而非主串

  • kmp算法,主要是要理解next数组的意义,以及求解过程,其过程与kmp的模板比较过程有异曲同工之妙。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值