[关闭]
@wzhang1117 2014-08-17T13:41:07.000000Z 字数 2024 阅读 6918

KMP算法求next数组

算法 KMP


KMP算法是一种快速模式匹配算法,其中最关键的部分是根据模式串生成next数组。

next数组的含义

设模式串为p0p1p2pi1,next数组为n0n1n2ni1.

nj=1,0,max(k),j=00<j<i,0<j<i,0<k<j,0<k<j,¬k,k,p0p1p2pk1=pjkpjk+1pj1p0p1p2pk1=pjkpjk+1pj1

1.暴力求解

这种方法的主要思路是根据next数组的定义将k从j-1到0暴力尝试,判断是否存在对应的序列。

  1. def ViolateGetNext(p):
  2. plen = len(p)
  3. n = [-1]*plen
  4. for j in range(1,plen):
  5. k = j - 1
  6. while k > 0:
  7. #如果存在长度为k的相同前缀和后缀
  8. if p[:k] == p[j-k:j]:
  9. break
  10. k -= 1
  11. #如果不存在这样的k,k此时为0
  12. n[j] = k
  13. return n

设模式串为
ABCDABD,n[5]的求解过程如下:
k=4
ABCDBCDA,k=3
ABCCDA,k=2
ABDA,k=1
A=A,n[5]=k

2.递归求解

暴力求解每次在计算n[j]时都是独立的,而实际上求解n[j+1]是可以利用到n[0,,j]的,递归算法就是基于这样的思路。
设模式串为p0p1p2pi1,已经计算出n0n1n2nj,如何计算nj+1?
1.k=nj,k为当前尝试的前缀后缀相等的最大值,即p0p1p2pk1=pjkpjk+1pj1
2.如果k==1,则nj+1=0,算法结束
3.如果pk==pj,则nj+1=k+1,算法结束
4.k<k,p0p1p2pk1=pjkpjk+1pj1=pkkpkk+1pk1,由定义可知k=nk
5.将k赋值给k,转到步骤2

  1. def ReGetNext(p,j):
  2. if j == 1:#j指示字符串的长度
  3. return [-1]
  4. #计算p前1到j-1个字符的next数组
  5. n = ReGetNext(p,j-1)
  6. k = n[j-2]
  7. while k != -1:
  8. if p[j-2] == p[k]:
  9. break
  10. k = n[k]
  11. #k+1为第j个字符next值
  12. n.append(k+1)
  13. return n

设模式串为
ABCDABD,n[5]的求解过程如下:
n=[1,0,0,0,0]=ReGetNext(p,5)
k=0=n[4]
P[4]=P[0](A=A),n=[1,0,0,0,0,k+1]

3.递归展开求解

这种方法和递归的思路类似,但是不如递归清晰。

  1. def GetNext(p):
  2. plen = len(p)
  3. n = [-1]*plen
  4. k = -1
  5. j = 0
  6. while j < plen -1:
  7. if k == -1 or p[j] == p[k]:
  8. j += 1
  9. k += 1
  10. n[j] = k
  11. else:
  12. k = n[k]
  13. return n

三种算法比较

计算next数组的关键还是在于彻底理解其含义。通过next数组的数学定义可以很容易的得到一个暴力解法,这个方法思路最清晰,第二种递归则是利用了next数组前后的关系得到了一个效率更高的算法,第三种和第二种类似,减少了递归调用。以模式串ABCDABD分析三种算法的效率,三种算法的字符比较次数分别为15,5,5

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注