KMP算法与Next数组

KMP算法

KMP算法的核心:是考虑到字串本身有重复字符,那么一旦出现匹配错误,就可以将子串右移到子串后边的第一个重复字符所在的位置,重新开始跟主串进行匹配。

KMP算法里的next数组,本质上是由子串中的字符重复数,进行了简单变形而得到的。

具体算法的思路如下

① 先计算子串后边的字符,有多少是与子串前缀重复的,将数量记录成数组repeat。

  假设子串为Word,当前子串的字符w的下标为j(即w = Word[j]),子串的当前字符要去与子串的前缀做比较:当前与子串前缀对比的思路→当前字符的重复数,建立在前个字符重复数的基础上。

  • 若前个字符与前缀不匹配(即repeat[j-1]=0),则当前字符w要与子串首字符比较(即Word[j]和Word[0]比较)

  • 若当前字符与前缀的第0位字符相同(即Word[j]=Word[0]),则令repeat[j]=1,表示当前字符与前缀的第0位匹配上了

  • 若当前字符与前缀的第0位字符不相同(即Word[j]≠Word[0]),则令repeat[j]=0,表示当前字符与子串前缀无法匹配

  • 若前个字符与前缀已匹配(即repeat[j-1]=k,且k≠0,表示当前字符w的前边,已经有k个与前缀对应匹配的字符)

    • 则当前字符w要与子串的第repeat[j-1]位做比较(即Word[j]和Word[repeat[j-1]]比较)

      • 若当前字符与前缀的第repeat[j-1]位字符相同(即Word[j]=Word[repeat[j-1]]),则令repeat[j]=repeat[j-1]+1,表示当前字符与前缀的第repeat[j-1]位匹配上了

      • 若当前字符与前缀的第repeat[j-1]位字符不相同(即Word[j]≠Word[repeat[j-1]]),表示当前字符与前缀的第repeat[j-1]位无法匹配,则令当前字符重新与子串前缀的第0位,重新开始比较,即(即Word[j]和Word[0]比较)

        • 若Word[j]≠Word[0],则令repeat[j]=0

        • 若Word[j]=Word[0],则令repeat[j]=1

如上图所示,在将主串与子串进行对比时,若第j位字符匹配不成功,(表示:前0~j-1位字符均已匹配成功,例如j=5,则子串中的Word[0\1\2\3\4],均与主串匹配成功,但Word[5]=D与主串匹配失败)

这时,应该将子串右移,直到子串的前缀移动到子串本身与前缀匹配的位置。

经过移动后,子串前缀AB就能来到冲突位之前,并且保证冲突位前的字符均与主串匹配上了,这样子串的j位就从5变为2,即Word[j]=Word[2]=C,由子串中的第2位C,继续与主串进行比较。

这里,子串要移动的下标位置j和repeat重复数,有个很巧妙的关系:j = repeat[j-1],也就是冲突位 j 的值,应变为前一位字符的repeat值。

(因为repeat[4]=2,子串当前冲突字符的前个字符的repeat[4]=2值,表示与前缀相同的字符长度为2,但下标是从0开始的,所以实际上repeat值,表示第0~repeat[j-1]位,即第0、1位的前缀都能匹配到冲突位之前的字符,那冲突位只要变为前缀的第repeat[j-1]位,即子串的第2位,Word[2]开始,继续与主串继续做比较)

Next数组

但是,考虑到获取冲突位的前一个字符的repeat值比较麻烦,因此一般可以直接把记录重复数的repeat数组,直接向右移动1位,变为next数组,用来表示当前字符发生冲突的时候,应将子串前边的第几位字符移到当前冲突位(即子串右移之后,当前冲突位变成子串的第几位下标),继续与主串进行比较。

初步得到的next数组,就可以表示,当冲突位j=5与主串冲突时,令j=next[j],即可令子串的C,与主串开始匹配(而此时C前的子串前缀AB,恰好与D前的AB相同,所以子串移动后,冲突位前的AB俩字符仍然保持不冲突)

但这时仍然要思考:如果子串移动后,原本子串冲突位的字符,仍然与移动后子串到达冲突位的字符,是一样的字符(如下图冲突位j=6时,将j=next[j]=2,但当j=2后,第2位字符F,仍然与主串发生冲突),应该怎么办?

应该继续向前回溯递归:即继续令j = next[j],直到子串移动后的冲突位的新字符,与主串相同,即Word[j]=Main[i],或者是已经回溯到子串的第0位字符为止仍冲突(即Word[j=0]≠Main[i],再回溯j = next[j]=next[0]=-1,表示已经回溯到子串的第0位字符)

  • 若子串移动后的冲突位的新字符,与主串相同Word[j]=Main[i],则子串Word与主串Main应匹配下一位,即当Main[i]=Word[j]时,令i++,j++

  • 已经回溯到子串的第0位字符为止仍冲突(即Word[j=0]≠Main[i],再回溯j = next[j]=next[0]=-1,表示已经回溯到子串的第-1位字符,无字符),则子串Word与主串Main应匹配下一位,即当j=-1时,令i++,j++

因此可见,无论是Word[j]=Main[i],还是j=-1,下一步的操作都是令i++,j++

  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值