java之KMP匹配算法

java之KMP算法


文章内容选自尚硅谷
参考了视频 「天勤公开课」KMP算法易懂版.
参考了博客 很详尽KMP算法(厉害).

算法理解

该算法的核心点在于怎么求next数组,这一点视频和博客讲的比较清楚了,但是新手肯定有很多误区,导致看不懂算法,现在简单说说新手常见的误区。

博客很详尽KMP算法(厉害).细分了最大长度表和next数组,本文是基于尚硅谷讲解的,尚硅谷中的next数组其实是博客中的最大长度表。故本文的next数组都理解成博客中的最大长度表。

  • KMP算法不像暴力匹配一样,一旦不匹配只移动一位,它之所以要移位,是先求出一段字符串的相同前后缀,然后把前缀的位置移到后缀的 位置上去。毕竟前后缀都相同嘛,中间跳过的位置必然是不匹配的。
  • KMP算法next数组每一位对应的值,都是从模式字符串首位置0到该索引位置,这个子字符串的最大相同前后缀的位数。这一步必须得理解。有可能next[i] = 2,next[i+1]=0,这是很正常的,因为string(子串)的后缀的最后一位与该子串的前缀的最后位不匹配了。如果next[i+1]=1,此处肯定是找到了原前缀字符串中具有相等前后缀的小前缀字符串。看不懂请多参考博客和视频。这是核心步骤。
  • 做KMP匹配时,是先求出 next数组,再进行匹配,即先根据模式串,求出和模式串字符串等长的next数组,把该数组每个值都求出来后,再根据数组进行查表移位。
  • 在求字符串的相同前后缀的时候,可不是把所有前后缀一一列出来比较,而是有独特的算法
	public static void KMPNext(String dest) {
		next = new int[dest.length()];
		next[0] = 0;
		//此处把i理解成后缀索引,j理解成前缀索引。前后缀索引的比较。
		for(int i = 1,j = 0;i < dest.length();i++) {
			while(j > 0 && dest.charAt(i) != dest.charAt(j)) {
				j = next[j - 1]; //大前后缀不匹配后,转而判断小前后缀是否匹配
			}
			if(dest.charAt(i) == dest.charAt(j)){
				j++;
			}
			next[i] = j;
		}
	}

求相同前后缀时,前后缀的匹配是在模式串中一位一位比较的,把i理解成后缀索引,j理解成前缀索引。一旦在模式串中dest.charAt(i) != dest.charAt(j) 即前后缀不匹配时,就要把前缀进行回溯,但并不是直接回溯到0,而是在大前缀中,以大前缀为基础,找大前缀字符串中前后缀相等的小前缀,回溯到小前缀的位置。如果一直不匹配才最终回溯到0位置。

代码演示

package com.atguigu.kmp;

import java.util.Arrays;

public class KMPAlgorithm {
	
	static int[] next ;
	public static void main(String[] args) {
		String str1 = "BBC ABCDAB ABCDABCDABCEABCDABD";
		String str2 = "ABCDABD";
		KMPNext(str2);
		System.out.println(Arrays.toString(next));
		int index = KMPSearch(str1,str2,next);
		System.out.println("index = "+index);
				
	}
	
	//在进行KMP匹配的时候,j是不断回溯的,而i是不断+1的,当j的长度==模式串的长度时,匹配成功
	public static int KMPSearch(String str1,String str2,int[] next) {
		for(int i = 0,j = 0;i < str1.length();i++) {
			while(j > 0 && str1.charAt(i) != str2.charAt(j)) {
				j = next[j - 1]; //如果不相等,要对模式串的j进行重置
			}
			if(str1.charAt(i) == str2.charAt(j)) {
				j++; //不能把j++提到上面的while里,因为上面的while退出循环的原因,可能是回溯到了0才 退出的。
			}
			if(j == str2.length()) {
				return i - j +1; //如果j不进行重置的话,i会比j小1,i-j+1刚好为0,因为i++for循环里面,所以j++比i++快一步,理解成i+1 -j
			}
			
		}
		return -1;
	}
	
	public static void KMPNext(String dest) {
		next = new int[dest.length()];
		next[0] = 0;
		//此处把i理解成后缀索引,j理解成前缀索引。前后缀索引的比较。
		for(int i = 1,j = 0;i < dest.length();i++) {
			while(j > 0 && dest.charAt(i) != dest.charAt(j)) {
				j = next[j - 1]; //大前后缀不匹配后,转而判断小前后缀是否匹配
			}
			if(dest.charAt(i) == dest.charAt(j)){
				j++;
			}
			next[i] = j;
		}
	}
}

运行结果为

[0, 0, 0, 0, 1, 2, 0]
index = 23

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值