[算法] 字符串匹配

2019-01-10  本文已影响0人  jingy_ella

问题定义

文本T[1..n] 模式P[1..m]
找到所有的偏移s(0<=s<=n-m),使得PT_{s+m}的后缀
前缀符号\sqsubset 后缀符号\sqsupset
后缀重叠引理 前缀和后缀关系为传递关系

运行时间

运行时间 = 预处理时间 + 匹配时间


朴素匹配算法

如果模式中所有字符都不相同,运行时间可达O(n*m)

Rabin Karp算法

PT_0, T_1...T_{n-m}预处理为以d为基数表示的数字:
P的预处理时间为\Theta(m)
T的预处理时间使用下式计算为\Theta(n-m+1):
t_{s+1} = (d(t_s - T[s+1]h) + T[s+m+1]) \;mod\; q
h = d^{m-1} \; mod \;q
如果t_s = p\; (mod \; q)说明该偏移s为伪命中点,可进行循环检测P[1..m] = T[s+1..s+m],最坏匹配时间\Theta((n-m+1)m),期望匹配时间O(n)

有限自动机匹配

符号定义

有限自动机M(Q, q_0, A, \Sigma, \delta)
后缀函数\sigma(x)是能作为x的后缀的P的最长前缀长度
\sigma(x)=max ( k: P_k \sqsupset x)

预处理阶段

构造模式串的字符串匹配自动机
状态Q={1,2..m},q_0为0,状态m为唯一接受状态(但m也需要求状态转移函数)
\delta(q, a) = \sigma(P_qa)

Compute-transition-function
m = P.length
  for q = 0 to m
    for each character a in \Sigma
     k = min(m, q+1)
     while P_k 不是P_q+a的后缀
        k--
     \delta(q,a) = k
return \delta

时间复杂度O(m^3 |\Sigma|)

COMPUTE-TRANSITION-FUNCTION(P, Σ)
    π = COMPUTE-PREFIX-FUNCTION(P)
    for a ∈ Σ 
        δ(0, a) = 0
    δ(0, P[1]) = 1
    for a ∈ Σ 
        for q = 1 to m 
            if q == m or P[q + 1] != a 
                δ(q, a) = δ(π[q], a)
            else
                δ(q, a) = q + 1
匹配阶段
Finite-automation-matcher
n = T.length
q = 0
for i = 0 to n-1 
  q = \delta(q, T[i])
  if q == m
    print i - m

时间复杂度\Theta(n)

Knuth Morris Pratt算法

符号定义

前缀函数 \pi[q]是能构成P_q后缀的P的最长前缀长度
\pi[q] = max(k : k < q 且 P_k \sqsupset P_q)

预处理阶段

摊还分析,时间复杂度\Theta(m)

Compute-prefix-function
m = P.length
pi[1] = 0
k = 0
  for q = 2 to m
    while k > 0 and P[k+1] != P[q]
      k = pi[k]
     if P[k+1] == P[q]
        k++
     pi[q] = k
return pi

注意!前缀函数所要求的真后缀是指k必须满足小于q,例如模式串首个字符的后缀本身必定也是它的前缀,但k==q,因此此处赋值为0(字符串从1开始, 如果从0开始应赋值为-1)

匹配阶段

摊还分析,时间复杂度\Theta(n)

KMP-Matcher
n = T.length
m = P.length
pi = Compute-prefix-function
q = 0
for i = 1 to n
  while q > 0 and P[q+1] != T[i]
    q = pi[q]
  if P[q+1] == T[i]
    q++
  if q == m
    print i - m
    q = pi[q]

Boyer Moore算法

上一篇下一篇

猜你喜欢

热点阅读