KMP算法------字符串匹配问题

KMP算法

应用场景——字符串匹配问题

有一个字符串str1 = “河南河南软件河南大学河南科技大学软件学院”和str2=“河南科技大学”

现在要判断str1是否含有str2,如果存在,就返回第一次出现的位置,如果没有,则返回-1

这是很多字符串匹配问题的例子

我们首先会想到的是暴力匹配

如果用暴力匹配的思路,并假设现在str1匹配到i位置,子串str2匹配到j位置,则有:

1.如果当前字符匹配成功( 即str1[i] == str2[j]),则i++, j++, 继续匹配下一个字符

2.如果失配(即str1[i]!= str2[j]),令i=i-(j-1), j=0。 相当于每次匹配失败时,i回溯,j被置为0

3.用暴力方法解决的话就会有大量的回溯,每次只移动一位,若是不匹配,移动到下一位接着判断,浪费了大量的时间。(不可行!)

package 字符串匹配问题;
public class ViolenceMatch {
    
    
	public static void main(String[] args) {
    
    
		// TODO Auto-generated method stub
		String str1 = "河南河南软件河南大学河南科技大学软件学院";
		String str2 = "河南科技大学";
		int index = violenceMatch(str1, str2);
		System.out.println(index);
	}
	//暴力匹配算法
	public static int violenceMatch(String str1,String str2){
    
    
		char[] s1 = str1.toCharArray();
		char[] s2 = str2.toCharArray();
		
		int s1Len = s1.length;
		int s2Len = s2.length;
		
		int i = 0;//指向s1
		int j = 0;//指向s2
		while(i<s1Len && j<s2Len){
    
    //保证匹配不越界
			if(s1[i] == s2[j]){
    
    
				//匹配成功
				i++;
				j++;
			}else{
    
    
				i = i - (j-1);
				j = 0;
			}
		}
		//判断是否匹配成功
		if(j == s2Len){
    
    
			return i - j;
		}else{
    
    
			return -1;
		}
	}
}

KMP算法介绍

KMP是一个解决模式串在文本串是否出现过,如果出现过,最早出现的位置的经典算法

KMP方法算法就利用之前判断过信息,通过一个next数组保存模式串中前后最长公共子序列的长度每次回溯时,通过next数组找到,前面匹配过的位置,省去了大量的计算时间

案例

有一个字符串str1= “BBC ABCDAB ABCDABCDABDE”,和一个子串str2=“ABCDABD”

现在要判断str1是否含有str2,如果存在,就返回第-次出现的位置,如果没有,则返回-1
要求:使用KMP算法完成判断,不能使用简单的暴力匹配算法.

文字叙述过程我就不写了,毕竟很多大佬写的很清楚了,这里有兴趣的可以去搜一下,我贴出来一个老哥的链接https://blog.csdn.net/dark_cy/article/details/88698736

package 字符串匹配问题;
import java.util.Arrays;
public class KMPAlgorithm {
    
    

	public static void main(String[] args) {
    
    
		// TODO Auto-generated method stub
		String str1 = "BBC ABCDAB ABCDABCDABDE";
		String str2 = "ABCDABD";
		
		int next[] = kmpNext("ABCDABD");  //[0,1]
		System.out.println("next"+Arrays.toString(next));
		int index = kmpSearch(str1, str2, next);
		System.out.println(index);
	}
	
	//获取到一个字符串(子串)的部分匹配值
	public static int[] kmpNext(String dest){
    
    
		//创建一个next数组保存部分匹配值
		int[] next = new int[dest.length()];
		next[0] = 0; //如果字符串是长度为1部分匹配值就是0
		for(int i = 1,j = 0;i < dest.length();i++){
    
    
			//当dest.charAt(i) != dest.charAt(j)
			//我们需要从next[j-1]获取新的j
			//知道我们发现有dest.charAt(i) == dest.charAt(j)成立才推出
			while(j > 0 && dest.charAt(i) != dest.charAt(j)){
    
    
				j = next[j-1];
			}
			//当dest.charAt(i) == dest.charAt(j)
			if(dest.charAt(i) == dest.charAt(j)){
    
    
				//部分匹配值就需要+1
				j++;
			}
			next[i] = j;
		}
		return next;
	}
	//写出KMP搜索算法
	/**
	 * 
	 * @param str1			原字符串
	 * @param str2			需要找的子串
	 * @param next			部分匹配表(子串对应的)
	 * @return				找到返回第一次出现的位置,没有匹配到返回-1
	 */
	public static int kmpSearch(String str1,String str2,int[] next){
    
    
		//遍历
		for (int i = 0,j = 0; i < str1.length(); i++) {
    
    
			//需要考虑不相等的情况    核心之处str1.charAt(i) != str2.charAt(j)
			while(j > 0 && str1.charAt(i) != str2.charAt(j)){
    
    
				j = next[j-1];
			}
			if(str1.charAt(i) == str2.charAt(j)){
    
    
				j++;
			}
			if(j == str2.length()){
    
    
				//找到了
				return i - j + 1;
			}
		}
		return -1;
	}
}

个人理解

KMP算法的难点就在于这个部分匹配值,关于这点得要自己去理解,有兴趣的可以在网上搜搜,与一般匹配方法相同的是,当字符相等的时候,都是往后移一位,继续去判断,不同于暴力破解那种方法的地方在于,当两个字符不相等的时候,暴力破解是把我们开始匹配的位置放到了刚才起点的后一个位置,然后从这个位置继续匹配一边,会很麻烦,而KMP算法是根据我们的部分匹配值的返回值给我们找好重新匹配的位置,如果你明白了这个匹配值怎么来的,你也就知道为什么要移动到那个位置,总的来说,KMP算法的核心就在于部分匹配值和下面这段代码

		while(j > 0 && str1.charAt(i) != str2.charAt(j)){
    
    
			j = next[j-1];
		}

也就是说当我们的子串匹配到位置的下标大于0,且原串下标为i的字符与当前子串位置为j的字符不相等的时候,我们要重新匹配,而这个位置是从我们的部分匹配表中的值取出来的,也就是说我们的i对应的原串一直在向后,没有想暴力破解那样在一个一个位置的回溯回来

猜你喜欢

转载自blog.csdn.net/qq_22155255/article/details/113888417