KMP字符串匹配

KMP字符串匹配_第1张图片

    我们假设有上图字符串S:abcaab和T:abcababcaabab字符匹配需求,左边是朴素算法:即从T的第0位和分别和S的1~n位开始比较,相同则用黄色标注,不同则用红色标注,如果遇到不同,然后T的第0位和下一位开始重新来过开始第二轮比较。但是,在这个比较的过程中,我们会发现两种情况:
    1.   0~3位分别相同,即T[0]=S[0],T[1]=S[1],T[2]=S[2],T[3]=S[3],同时T[0]!=T[1]!=T[2],所以显然T[0]!=S[1]!=S[2],所以显然T[0]和S[1],S[2]的比较是没有必要的,所以对于KMP来说,第二轮和第三轮的比较的次数是都是0!
    2.   然后,我们再看第四轮比较,我们同时发现,由于abca,前一位和后一位是相同的,所以当开始第三轮的时候,T[0]=S[3],这次比较也是多余的,直接比较T[1]?=S[4],发现相等,T[2]?=T[5],发现不等,跳到下一轮。

    这两种情况所带来的效果就是:本来遇到不匹配的位置T[j]!=S[i]时是应该“回溯”到T[0],用T[0]与上次和T[0]比较的S[n]下一位S[n+1]开始比较的,可是由于T串自身的一些特点,使得T不用“回溯”到T[0],而是“回溯”到T[j],然后继续和S[i]比较,所以问题来了:这个位置j到底是如何规定的?

    “回溯”是KMP算法的的关键所在,求出回溯点j也就是找到了KMP的核心所在。

    当T[4]!=S[4]时,由于第一种情况,所以首先T[0]不必和S[2],S[3]相比,又由于第二种情况,所以不必回溯到T[0],而只需回溯到T[1]?=T[4]。所以第一种情况决定了S[i]不用回溯到S[n+1],第二种情况决定了T[j]不用回溯到T[0],而这个T[1]中的j=1,就是出现不匹配的时j位置之前,即T[0]~T[j-1]有多少相同的前后缀,例如我们假设是在T[5]的时候出现的不匹配T[5]!=S[5],则下一轮比较,只需比较T[2]?=T[5],这个2就是T[0]~T[4]之间有两个前后缀相同!所以我们要做的就是对于T,求出每一个位置之前有多少相同的前后缀:

    首先:规定T[0]=-1,T[1]由于1位置之前只有一个元素,所以T[1]=0
    然后:每有一个后缀和前缀元素相同,假设此时后缀位置为i,则T[i+1]回溯值为此时回溯值加1,同时开始比较i+1和j+1,我们通常把next[i]称为回溯的位置值。实际上此时n位前缀和n位后缀也处于上述S和T的比较过程中,例如对于ababaaaba:
这里写图片描述
    在T[5]时,因为T[0~2]=T[2~4],求出next[5]=3时,接下来要求next[6],即比较(T[3]=b) ?= (T[5]=a),此时显然不同!那我们接下来要做什么逻辑呢来判断next[6]呢?
这里写图片描述

    此时,next[5]的求解处于上图中的位置,由文章起始讲的“回溯”可知,接下来要比较的是T[j=1]?=T[j=5],发现不等,则比较T[j=0]?=T[j=5],然后相等,则认为next[6]=j+1=1,然后依次类推求解接下来的值!在这个过程中j=3—>j=1—>j=0,也是回溯的问题,而此时回溯点已经在之前求出来了!

    说了这么久,我们来看看实际的代码是怎样的!
    对于next值:

KMP字符串匹配_第2张图片
    对于匹配函数:
KMP字符串匹配_第3张图片

你可能感兴趣的:(数据结构与算法)