美文网首页
[算法] 字符串匹配

[算法] 字符串匹配

作者: jingy_ella | 来源:发表于2019-01-10 10:49 被阅读0次

    问题定义

    文本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|)

    • 利用kmp中的π求此处的转移函数可以将时间复杂度降低为​O(m|\Sigma|)
      因为\delta(q, a) = \sigma(P_qa),所以有\delta(\pi[q], a) = \sigma(P_{\pi[q]}a),而\pi[q] = max(k : k < q 且 P_k \sqsupset P_q)表示能构成P_q真后缀的P的最长前缀长度,\sigma(P_qa)=max ( k: P_k \sqsupset P_qa)表示能构成P_qa的后缀的P的最长前缀长度,因此,\sigma(P_qa) = \sigma(P_{\pi[q]}a)。同时如果q!=mP[q+1]=a那么\delta(q,a)直接等于q+1即可,综合可得,如果 q == mP[q +1] !=a那么​\delta(q,a) = \delta(\pi[q],a)。根据上式利用前缀函数​\pi只需m次循环便可计算得到转移函数,时间复杂度为​O(m|\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算法

    相关文章

      网友评论

          本文标题:[算法] 字符串匹配

          本文链接:https://www.haomeiwen.com/subject/ryxnrqtx.html