字符串匹配 KMP算法

问题描述:字符串匹配即查找待匹配字符串(模式串)p在主串s中的位置。一般处理这种问题往往采用简单粗暴的方法——暴力匹配法。所谓暴力匹配法,就是对主串s的每一个字符与要匹配的字符串p的每个字符进行逐一匹配。但暴力匹配效率比较低,经典算法KMP效率更高。

例:主串s=“abcdeabcdxabc”,模式串p=“abcdx”。结果应该返回5。

暴力匹配的图解如下:
在这里插入图片描述
代码如下:


public class 字符串匹配暴力破解 {
	public static void main(String[] args){
		String s = "abcdeabcdxabc";
		String p = "abcdx";
		System.out.println(indexOf(s, p));
	}

	private static int  indexOf(String s, String p) {
		int i=0;  //主串匹配的起点下标
		int j=0;	
		int sc=i;  //主串的移动下标 
		//当s剩余的长度小于p时就,p就不可能是s的子串了
		while(i<s.length()-p.length()){
			if(s.charAt(sc)==p.charAt(j)){
				sc++;
				j++;
				if(j==p.length()) return i;
			}else{
				i++;
				sc = i;
				j = 0;
			}
		}
		return -1;
	}
}

KMP算法最重要的是得出next数组,next数组的长度和匹配字符串P的长度是相同的。next数组表示当出现失配时应该回溯到那个位置。next数组前两个是确定的,分别是-1和0;-1表示第一个失配时应该i进行自增j从0开始,0表示第二个出现失配j应该回溯到0。KMP算法的特征之一是i不会回溯,正是因为如此才提高了效率。
next数组就是失配位置之前的字符串的前缀和后缀的最大匹配数。
KMP算法的图解如下:
在这里插入图片描述
next数组的求法:
例如:字符串bababb的next数组应该是{-1,0,0,1,2,3},图解如下,j表示出现失配的位置,k表示j出现失配时应该回溯到的位置。因为前两个-1和0是固定的,所以可以根据前两个递推出后面的回溯值。规律如下:如果p[k]==p[j]或者k小于0,next[j+1] == k+1; 然后j++; k++; 如果不同k继续回溯(k=next[k]), 直到p[k]==p[j]或者k小于0
在这里插入图片描述

代码如下:


public class 字符串匹配KMP算法 {
	public static void main(String[] args){
		//String s = "abcdeabcdxabc";
		//String p = "xabc";
		String s = "babababcbabababb";
		String p = "bababb";
		System.out.println(KMP(s, p));
	}

	private static int KMP(String s, String p) {
		if(s.length()<0||p.length()<0) return -1;
		if(s.length()<p.length()) return -1;
		int j = 0;
		int i = 0;
		//int[] next = {-1,0,0,0,0};
		int[] next = next(p);
		//kmp算法i是有可能移动到字符串s的后面的。
		//所以跳出循环的条件不应该是i <= s.length() - p.length()
		while (i < s.length()){
			if(j==-1 || s.charAt(i)==p.charAt(j)){
		//让i往后移(自增)的有两种情况,匹配成功,i和j同时后移
		//第二种是在第一个位置就出现失配		
				j++;
				i++;
			}else{
				j = next[j];
			}
			if(j==p.length()) return (i-j);
		}
		return -1;
	}

	private static int[] next(String ps) {
		int len = ps.length();
		int[] next = new int[len];
		char[] p = ps.toCharArray();
		next[0] = -1;
		if(len==1) return next; //防止数组越界
		next[1] = 0;
		int j = 1;
		int k = next[j];
		while(j < len - 1){//当next数组最后一位完成赋值后结束循环
			if(k<0 || p[j] == p[k]){
				next[++j] = ++k;
			}else{
				k = next[k];
			}
		}
		return next;
	}
}

下面是KMP算法的变形,返回字符串p在字符串s中出现的次数,改变匹配成功时的代码即可。i退一格,j=next[j-1]; 也就是说j退回(p.length()-1)位置(也就是字符串的最后一位)失配时应该回溯的位置。
代码如下:


public class 字符串匹配KMP算法 {
	public static void main(String[] args){
		//String s = "abcdeabcdxabc";
		//String p = "xabc";
		String s = "babababcbabababb";
		String p = "ba";
		System.out.println(KMP(s, p));
	}

	private static int KMP(String s, String p) {
		if(s.length()<0||p.length()<0) return -1;
		if(s.length()<p.length()) return -1;
		int count =0;
		int j = 0;
		int i = 0;
		//int[] next = {-1,0,0,0,0};
		int[] next = next(p);
		//kmp算法i是有可能移动到字符串s的后面的。
		//所以跳出循环的条件不应该是i <= s.length() - p.length()
		while (i < s.length()){
			if(j==-1 || s.charAt(i)==p.charAt(j)){
		//让i往后移(自增)的有两种情况,匹配成功,i和j同时后移
		//第二种是在第一个位置就出现失配		
				j++;
				i++;
			}else{
				j = next[j];
			}
			if(j==p.length()){
				count++;
				i--;
				j = next[j-1];
				//return (i-j);
			}
		}
		return count;
		//return -1;
	}

	private static int[] next(String ps) {
		int len = ps.length();
		int[] next = new int[len];
		char[] p = ps.toCharArray();
		next[0] = -1;
		if(len==1) return next; //防止数组越界
		next[1] = 0;
		int j = 1;
		int k = next[j];
		while(j < len - 1){//当next数组最后一位完成赋值后结束循环
			if(k<0 || p[j] == p[k]){
				next[++j] = ++k;
			}else{
				k = next[k];
			}
		}
		return next;
	}
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
KMP算法是一种用于字符串匹配算法,它的核心思想是利用部分匹配表来避免不必要的比较。下面是KMP算法的原理和实现步骤: 1. 部分匹配表的计算: - 部分匹配值是指字符串的前缀和后缀的最长公共部分的长度。 - 部分匹配表是一个数组,记录了每个位置的部分匹配值。 - 部分匹配表的计算可以通过动态规划的方式进行,具体步骤如下: - 初始化部分匹配表的第一个元素为0。 - 从第二个元素开始,依次计算每个位置的部分匹配值: - 如果当前位置的字符与前一个位置的部分匹配值对应的字符相等,则部分匹配值加1。 - 如果不相等,则需要回溯到前一个位置的部分匹配值对应的字符的部分匹配值,继续比较。 - 在主串从左到右依次比较字符,同时在模式串根据部分匹配表进行跳跃。 - 如果当前字符匹配成功,则继续比较下一个字符。 - 如果当前字符匹配失败,则根据部分匹配表找到模式串需要跳跃的位置,继续比较。 下面是一个使用KMP算法进行字符串匹配的示例代码: ```python def kmp_search(text, pattern): n = len(text) m = len(pattern) next = get_next(pattern) i = 0 j = 0 while i < n and j < m: if j == -1 or text[i] == pattern[j]: i += 1 j += 1 else: j = next[j] if j == m: return i - j else: return -1 def get_next(pattern): m = len(pattern) next = [-1] * m i = 0 j = -1 while i < m - 1: if j == -1 or pattern[i] == pattern[j]: i += 1 j += 1 next[i] = j else: j = next[j] return next ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥自在”

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值