zoukankan      html  css  js  c++  java
  • KMP算法的学习经验

    KMP算法的学习经验

    (欢迎指正错误, 欢迎喷)

    1. 什么是kmp(完)
    2. kmp的额外知识(完)
    3. 暴力匹配的缺点,和代码实现(完)
    4. next[]数组的预先知识,了解前后缀,相同前后缀。(完)
    5. kmp的关键next[]数组, next的实现。(完)
    6. next数组中k = next[k]的理解。
    7. kmp的优化(待续。。)

    一.什么是kmp

    KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。时间复杂度O(m+n)
    ————来自百度百科
    简单的说 :
    1.kmp是一种字符串匹配算法。
    2.它的优点就是能通过 next[]数组(一个记忆数组),减少匹配的次数, 从而节省时间。
    3.关键是next[] 这个记忆数组。

    二.kmp的额外知识

    由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)
    并且kmp被很多人叫做,看(k)毛(m)片(p)算法。

    三.普通暴力匹配


    //ptr[] 匹配串, str[] 主串

    1.暴力匹配
    我们的普通暴力匹配是 匹配串ptr 的字符一个一个和 主串匹配。
    如果一个字符匹配成功:ptr的下一个字符继续匹配,
    如果失败 :str[]回溯到开始匹配的位置(i = i - j, <-下面有讲解),然后再以 下一个位置为开始, 再次重新进行字符串匹配

    暴力匹配代码:

    int plen = strlen(ptr);//匹配串的长度
    int slen = strlen(str);//主串的长度
    int i, j = 0; //i 和j 分别代表 当前 str 和ptr 所匹配的字符下标
    int flag = 0;//如果为 1 表示 主串中存在 匹配串。
    for(i = 0;i < slen;i++)
    {
    	if(str[i] == ptr[j])//如果匹配成果 继续下一个匹配
    	{
    		j++;
    	}
    	else//如果 不相等 
    	{
    		i = i- j;//主串回溯, i回到匹配前的位置
    		j = 0;//匹配串 要从头开始
    	}
    	if(j == plen)// 主串中存在客串 匹配串成功
    	{
    		flag = 1;
    		break;//结束
    	}
    }
    

    以上是暴力匹配 的代码, 这也能解决字符串匹配的问题(这种方法没有一点错误), 然而你用这种方法去解题,很多题只能送你个:
    在这里插入图片描述
    acmer的你,看到这就是很尴尬了。在这里插入图片描述
    为什么呢?
    因为你暴力匹配太盲目了, 而kmp确实聪明的 kmp的j知道往哪跳。

    四. 相同前后缀的讲解:

    1.什么是前后缀。
    如:ABCD这个字符串
    A
    AB
    ABC
    就是他的前缀。

    BCD
    _CD
    __ D
    就是它的后缀。
    ·下划线只是营造效果, 没有意义。

    2.相同前后缀:
    如:ABCAB这个字符串
    AB(前缀) AB(后缀) 就是相似前后缀(而且AB是最长的相似前后缀)。

    五.kmp的next数组

    kmp 通过next数组, 保留了ptr(匹配串)以每个字符结尾的子串的 最长相同前后缀的长度。通过next 主串就不用回溯, 只需ptr(匹配串)来回的跳(有想法的跳 o(* ̄︶ ̄ *)o)就行。
    {
    如:1. 主串ABCABCABD 客串ABCABD
    2.当匹配到:
    ABCABCABD
    ABCABD 与其失配。
    但是我们已经匹配了ABCABCABD 这个AB 我们已经知道他匹配,
    所以根据ptr的 前后缀相似的性质(ABCABD), 直接跳到相似前缀的哪里 接着往后匹配。
    所以就要知道next数组怎么获得。

    }

    那么接下来就看个有图的栗子(例子):
    主串:ABABAC
    匹配串: ABAC
    
    A B A B A C
    A B A C

    在这里插入图片描述

    /***next数组的获取*/
    int Get_next()
    {
        int plen = strlen(ptr);
        next[0] = -1;
        int k = -1;//表示上一次匹配的最长前后缀长度
        for(i = 0; i < plen; i++)
        {
            if(k == -1 || ptr[i] == ptr[k])// ptr[i]是后缀的对后一个字符, ptr[k]是前缀的最后一个字符。 k = -1是以第一个字符结尾的子字符串没有前后缀,因此就要有个操作, 解决 第一个字符串就匹配失败的情况。
            {
                k++;//如果本次匹配成功 , 长度加一
                next[i+1] = k;//数组储存
            }
            else//不成功
            {
                i--;//主串不动
                k  = next[k];//长度缩减再次匹配, 对于k为什么等于next[k] ,后续会有讲解。
            }
        }
    }
    /***kmp 匹配过程****/
    int kmp(char str[], char ptr[]{
        int flag = 0; // flag = 1 表示 主串中含有 匹配串
    	int slen = strlen(str);
    	int plen = strlen(ptr);
    	int i, j = 0;
    	for(i = 0;i < slen;i++)
    	{
    		if(j == -1||str[i] == ptr[j])
    		//j == -1是:当第一个字符串就不匹配时, 整个匹配串要整体往右移一个。
    		{
    			j++;
    		}
    		else 
    		{
    			i--;
    			j = next[j];
     //匹配失败 j 跳到一上一个字母结尾的前缀后面+1处,
    		}
    		if(j == plen)
    		{
    		  flag = 1;
    		  break;
    		}
    	}
    	return flag;
    }
    

    六. k = next[k]的理解:

    其他大佬对k = next的理解入口->
    本人的讲解:
    当匹配失败时 主串 i会停留不动,
    k 会都等于 next[k]。

    一下图片是 AAACAAAA和它next[]数组情况正确

    接下来是过程
    在这里插入图片描述

    深入了解kmp 请看大牛的讲解->

  • 相关阅读:
    抓鱼社区
    DotNetTextBox V3.0 所见即所得编辑器控件Ver3.3.3 Free(免费版)
    Pet Shop 4.0系统
    北大青鸟PPT/ Net学习课件/asp.net,AJAX,ADO.net视频教程
    [开源]ASP.NET通用权限管理系统(FrameWork) 1.0.1 Release
    sql isnull函数
    几种常见算法的介绍及复杂度分析(转)
    ajaxpro配置与使用
    C#对图片的几种简单处理
    [你必须知道的.NET] 开篇有益
  • 原文地址:https://www.cnblogs.com/TJack/p/10526956.html
Copyright © 2011-2022 走看看