ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

与KMP算法的相爱相杀之-----深刻理解记忆KMP算法(祥解)

2021-09-21 11:57:51  阅读:212  来源: 互联网

标签:String sonStr ++ 相杀 next int 算法 KMP else


引言:今天被这道题整笑了,也被这道题打醒了,原来我还没有真正的理解KMP算法
先来讲讲这道题有多有趣先:
在这里插入图片描述
一:
对于熟悉了解面向对象的封装性来说,解决这道题只需要一行代码哈哈哈有被笑到,以下是Java代码:

return haystack.indexOf(needle);

在这里插入图片描述
OK今天的面试到此结束,可以回去等通知了

二.
暴力枚举:每一个都进行比较

class Solution {
    public int strStr(String haystack, String needle) {       
        if(needle.equals("")) return 0;
        int slow, fast, m = haystack.length(), n = needle.length(), k;
        for(slow = 0; slow < m - n + 1; slow++){
            k = slow;
            for(fast = 0; fast < n; fast++){
                if(haystack.charAt(k) != needle.charAt(fast))
                break;
                k++;
            }
            if(fast == n) return slow;
        }
        return -1; 
    }
}

在这里插入图片描述
虽然都可以过,但性能太差,所以下面主角登场---------KMP算法,专治各种子串匹配:
说来惭愧,在使用它前我又忘了怎么算,于是又去翻了资料,于是出于胜负欲的刺激下,我这次要完完整整踏踏实实深深刻刻的干了KMP算法,并且做好笔记,理解每一处细节

下面先引入俩个概念:getNext数组要用到
前缀:包含第一个字符且不含最后一个字符
后缀:包含最后一个字符且不含第一个字符
注:前缀串与后缀串的对比都是从左到右!!
声明:下面可以跟着然后自己手算多练习几个,对理解后续的代码有很大帮助

1.首先,获取next[] 数组,下面我直接上图吧,扣字描述费键盘
在这里插入图片描述
需要的话就多手写几个例子,手写不难,主要是代码是理解较费劲
然后直接上代码:

    public static void getNext(int[] next, String sonStr){
        int len = sonStr.length();
        char[] ch = sonStr.toCharArray();
        int i = 0, k = 1;/*求从第三个起的前后缀相同字符个数,
                         //i表示前缀开始,k表示后缀开始(即每次的j-1)
                         因此第j个的表示是:k+1   */
        next[0] = -1;
        next[1] = 0;
        while(k < len - 1 ){
            if(i != -1 && ch[i] == ch[k]){
                next[k+1] = i + 1;/*
                                 这里就是第j个的赋值,
                                 因为i从0开始,所以计算个数要+1 
                                 */
                i++;
                k++;               
            }
            else if(i == -1){
                next[k + 1] = 0; 
                /*如果i == -1,就是说明第j个的前后缀没有相同的字符,就赋值0然后继续比较下一个,所以k++
              那么前缀继续由第一个开始,即下标0,所以i++ 
               */      
                i++;
                k++;
            }
            else{
            //这里是重点,i = next[i],我个人认为有动态规划的嫌疑
            //因为此处前后缀不匹配,但也许前缀的i倒退几格就能与后缀匹配上了
            //因此要借助前面的值才知道我们的i要推回哪里,回到-1就说明匹配不上,因此有动态规划的嫌疑
                i = next[i];
            }
        }
        
    }
   

下面开始分析:(代码有注释,可以看)
一:Next数组有俩个特数值:next[0] = -1, 与next[1] = 0,每一次求next[]可以直接先赋值,因为next[1]前只有一个字符,所以他即包含了第一个字符且含最后一个符,因此比不了(根据前缀和后缀的定义)。
所以也说明了子串要超过2个字符才能调用该方法,否则报错
二:
该方法求的是第 j 个,但要从第j -1个开始,而j -1又得看 j - 2的结果所以有动态规划的嫌疑,

详解可看上面代码的讲解

最后附上完整调试代码:

package FirstPackage;

public class KMP {

	public static void main(String[] args) {
		String fatherStr = "mississippi";
		String sonStr = "issip";
		System.out.println(testKMP(fatherStr,sonStr));
		

	}
	public static int testKMP(String fatherStr,String sonStr) {
		int n = fatherStr.length(), m = sonStr.length();
		if(m == 0) return 0;
		else if(m < 2) {
			return fatherStr.indexOf(sonStr.charAt(0));
		}
		else {
		int[] next = new int[m];
		getNext(next, sonStr);
		int i = 0, j = 0;
		//while循环一定要这么写,俩者一个不成立都不行,否则会报下标越界的错误
		while( j < n && i < m ) {
			if(i == -1 || fatherStr.charAt(j) == sonStr.charAt(i)) {
				i++;
				j++;
			}
			
			else i = next[i]; //不匹配 i 就回到i该回去的地方
		}
		if(i == m) return j - i; //举例手算一下,就是这个值
		else return -1;		
		}
	}
	
    public static void getNext(int[] next, String sonStr){
        int len = sonStr.length();
        char[] ch = sonStr.toCharArray();
        int i = 0, k = 1;
        next[0] = -1;
        next[1] = 0;
        while(k < len - 1 ){
            if(i != -1 && ch[i] == ch[k]){
                next[k+1] = i + 1;
                i++;
                k++;               
            }
            else if(i == -1){
                next[k + 1] = 0;
                i++;
                k++;
            }
            else{
                i = next[i];
            }
        }
        
    }
   

}

然后我们再提交一下结果,看看KMP的性能:
测试代码如下:–这里所有可能情况都一一考虑到了

class Solution {
    public int strStr(String haystack, String needle) {
      	int n = haystack.length(), m = needle.length();
		if(m == 0) return 0;
		else if(m < 2) {
			return haystack.indexOf(needle.charAt(0));
		}
		else {
		int[] next = new int[m];
		getNext(next, needle);
		int i = 0, j = 0;
		while( j < n && i < m ) {
			if(i == -1 || haystack.charAt(j) == needle.charAt(i)) {
				i++;
				j++;
			}			
			else i = next[i];
		}
		if(i == m) return j - i;
		else return -1;		
		}
        
    }

        public static void getNext(int[] next, String sonStr){
        int len = sonStr.length();
        char[] ch = sonStr.toCharArray();
        int i = 0, k = 1;
        next[0] = -1;
        next[1] = 0;
        while(k < len - 1 ){
            if(i != -1 && ch[i] == ch[k]){
                next[k+1] = i + 1;
                i++;
                k++;               
            }
            else if(i == -1){
                next[k + 1] = 0;
                i++;
                k++;
            }
            else{
                i = next[i];
            }
        }
        
    }
    }

在这里插入图片描述
哇giao,大佬们研究的算法就是不一样。而对于我这种菜鸟,只需要站在巨人的肩膀上,记住和学习他们的思想就行了

好了,文章到这里终于结束,希望这次能真正地悟道巨人们的思想,资料不再翻来翻去

标签:String,sonStr,++,相杀,next,int,算法,KMP,else
来源: https://blog.csdn.net/XJ200012/article/details/120399205

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有