KMP算法整理

一、KMP算法的作用

减少常规暴力算法中不必要的回溯。

二、明确什么是字符串的前缀、后缀

以下面字符串str举例

a a b d a a b

前缀后缀
ab
aaab
aabaab
aabddaab
aabdabdaab
aabdaaabdaab
aabdaabaabdaab

三、减少回溯的方式

通过使用next数组,使得被模板字符串和主串失配的地方可以快速回溯到上一处具有最长公共前缀后缀长度的地方,同时避免回溯到主串的源头。以此减少时间复杂度。

四、如何生成next数组

首先给出模板字符串m:abaabbabaab
主串s:abaabaabbabaaabaabbabaab
定义一个数组next
int next[] = new int [s.length];
下面需要明确一个定义,即什么叫做最大公共前缀后缀长度。
下面我们就拿m串进行举例,我们从m串为a、ab、aba,逐渐增加到abaabbabaab来观察和理解最大公共前缀后缀长度的含义。
红色字体标记为当前的最长公共前缀后缀

next数组
next[0]=-1m串最大公共前缀后缀长度
next[1]=0a0
next[2]=0ab0
next[3]=1abaa (1)
next[4]=1abaaa (1)
next[5]=2abaabab (2)
next[6]=0abaabb0
next[7]=1abaabbaa (1)
next[8]=2abaabbabab (2)
next[9]=3abaabbabaaba (3)
next[10]=4abaabbabaaabaa (4)
Next数组不可越界所以无next[11]abaabbabaababaab (5)
index012345678910
next-10011201234

在上表中可以看到,我们将m串的最大公共前缀后缀长度存储到了next数组中。那么他们的意义到底是什么呢?
意义为:如果模板串在与主串匹配过程中发生了失配情况,那么我只需要将模板串倒退一定位数就可以继续进行匹配,以此来避免回溯主串。而这个模板串回退到的位置就是next数组中存储的元素。

五、如何使用next数组

我们举例说明一下,假设我的模板串abaabbabaab在最后一个字符b的匹配过程中出现了失配,我们就可以利用next数组,将模板串回退到next[next[10]],即next[4]处abaabbabaab,然后再次与之前失配的地方再次进行匹配,如果成功了就继续匹配下一个模板串的字符,如果没有成功,就再次执行上述的回退操作。详见下图

六、伪代码实现KMP算法

1、生成next数组

cn:=0; next[0]:=-1; next[1]:=0;

while(j遍历到模板串尾或cn回溯到-1){
if(m[i-1]==m[cn]){
next[i]=++cn;
i++;
}else if(m[i-1]!=m[cn]){
cn=next[cn];//cn作为游标开始进行回溯
}考虑情况如果游标回溯到-1
if(。。。){
游标回到-1说明此过程中没有能够和当前后缀匹配的前缀。故需要更新最大长度
}
}

2、KMP匹配过程

建立遍历主串用的指针i、遍历模板串使用的指针j
while(i<主串长度且j小于模板串长度){
if(m[j]==s[i]){
说明当前字符匹配,继续匹配下一个
i++;
j++;
}else if(不相等){
利用上面获取的next数组,使j回溯。
同属需要考虑到如果j回溯到j=-1的情况时,应该跳过当前i重新开始对下一个i进行匹配。
}

七、java语言实现

import java.util.*;

public class KMPtest {

	static String str;
	static String m;
	static int next[];
	static char strs[];
	static char ms[];
	static int cn = 0;

	public static void main(String[] args) {

		Scanner in = new Scanner(System.in);
		str = in.nextLine();
		m = in.nextLine();
		strs = str.toCharArray();
		ms = m.toCharArray();
		next = new int[m.length()];

		int k = 2;
		next[0] = -1;
		next[1] = 0;
		while (k < ms.length && cn != -1) {

			if (ms[k - 1] == ms[cn]) {//用于建立next数组
				next[k] = ++cn;
				k++;
			} else if (ms[k - 1] != ms[cn]) {
				cn = next[cn];
			}
			if (cn == -1) {
				next[k] = 0;
				cn = 0;
				k++;
			}
		}
		
		int i = 0;// 用于遍历主串
		int j = 0;// 用于遍历模板串
		
		while (j < ms.length && i < strs.length) {//进行模板串和主串的匹配

			if (strs[i] == ms[j]) {
				j++;
				i++;
			} else if (strs[i] != ms[j]) {
				if (next[j] == -1) {
					i++;
				} else {
					j = next[j];
				}
			}
		}
		System.out.println("匹配位置为:" + (i - j + 1));
	}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值