KMP算法理解

给出问题:在字符串(主串)中查找子串(模式串),如给定长度为m字符串A =“aababcaabaacdcbab”,长度为n的模式串B=“aabaacd”,给出B在A中的位置。

暴力解法的时间复杂度为O(n*m),而KMP算法的时间复杂度为O(n+m)

一、基本概念

子序列:字符串中任意个相对位置不变的字符组成的字符串,如“ab”,“ac”,“a”均为“abc”的子序列。

子串:字符串中任意连续个字符组成的子序列,如“ab”,“bc”,“abc”,“”均为“abc”的子串。

字符串前缀包含第一个字符,但不包含最后一个字符的子串,如“a”,“ab”,“abc”均为“abcd”的前缀。

字符串后缀:不包含第一个字符,但包含最后一个字符的子串,如“d”,“cd”,“bcd”均为“abcd”的后缀。

部分匹配值:字符串前缀集合和字符串后缀集合中最长公共元素的长度。如字符串“abab”的前缀集合为{“a”,“ab”,“aba”},后缀集合为{“b”,“ab”,“bab”},公共元素仅有“ab”,因此其为最长公共元素,长度为2。

二、逻辑思路

从主串起始位置的字符(i=0,S[i]='a')开始与模式串起始位置字符比较(j=0,P[j]='a'),相等则继续比较主串和模式串中的下一字符,直到完全匹配或某一位置不匹配为止。当不匹配时,如主串i=4,S[i]='b'与模式串j=4,P[j]='a'不匹配,则已匹配字符串为“aaba”,其部分匹配值为1,即字符串“aaba”从起始位置数起的前1个字符和从末尾位置数起的前1个字符相同。那么i指向的位置不需回退,只需将j指向位置的序号改为部分匹配值1即可(若部分匹配值为-1,则i++),然后重新开始匹配。

next数组的大小与模式串相同,其next[j]的含义是P[j]之前的字符串P[0..j-1]的部分匹配值。

根据定义求得next数组,如表

Index

0

1

2

3

4

5

6

模式串

a

a

b

a

a

c

d

当前位置的最长前缀

a

aa

aab

aaba

aabaa

aabaac

最长前缀的部分匹配值

0

0

1

0

1

2

0

next数组值

-1

0

1

0

1

2

0

例:

S[4]!=P[4],则j=next[j]=next[4]=1,即

S[4]!=P[1],则j=next[j]=next[1]=0,即

S[4]!=P[0],且因为next[0]=-1,则i=i+1,即

不断重复上面的过程,直到完全匹配或i超过主串长度。

三、求解next数组

(1)j=0,1时,根据定义任意模式串的next值是相同的,均为-1和0。

(2)j=2时,next值=”aa”的部分匹配值

”aa”的部分匹配值的求解可转换为求解pattern[next[j-1]]= pattern[j-1]是否相等

next[j-1] = 0,表示序号为j-1的字符”a”的部分匹配值为0,即 黄色部分的字符串相同,且长度为0。

pattern[j-1] = ‘a’ 表示序号为2的字符的前一个字符。

pattern[next[j-1]] = ‘a’表示第一个黄色字符串后紧挨着的一个字符。

因此,pattern[next[j-1]]== pattern[j-1]?即 两括号部分的字符串是否相等?

若两部分相等,则next[j] = next[j-1]+1

若两部分不相等,则分两种情况讨论:

  1. 黄色部分字符串长度next[j-1]==0,那么next[j]=0。即黄色部分字符串不存在,而pattern[j-1]与pattern[next[j-1]] 对应的字符又不相等,则当前的next值为0。
  2. 黄色部分字符串长度next[j-1]!=0,那么问题就进一步转换为求解pattern[next[next[j-1]]]= pattern[j-1]是否相等。此种情况见j=3。

这里,两部分相等,因此next[j] = next[j-1]+1 = 1

(3)j=3时,next值=”aab”的部分匹配值

同理,转换为求解pattern[next[j-1]]= pattern[j-1]是否相等。

next[j-1] = 1

pattern[j-1] = ‘b’

pattern[next[j-1]] = ‘a’

因此,pattern[next[j-1]]== pattern[j-1]?即两括号部分的字符相等?(黄色部分为字符串"aa"的部分匹配值对应的相同字符串)

这里不相等,因此进一步转换为求解pattern[next[next[j-1]]]= pattern[j-1]是否相等?即两括号部分的字符串是否相同?

蓝色区域表示黄色区域字符的部分匹配值的长度,因为两黄色区域的字符串相同,因此两蓝色区域的字符串也相同。

这里两括号部分的字符串不同,且next[next[j-1]]=0.,因此next[j] = 0。

(4)同理可得j=4、5、6下的next值1、2和0。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值