串的模式匹配算法

在代码中的模式匹配往往都是写好的接口, 基本上只需要用一句代码可以实现.


匹配字符串中的子字符是如何实现的呢? 假设我们以 S 代表目标串,即父串(例如 aeaaefababdc), 以 T 代表模式串即子串(例如 abd), i 代表 S 中字符位置, j 代表 T 中字符位置, 即下标. 我们要做的是匹配是否 abcdefabdc 中存在 aeaad.

  • 最常用的是使用朴素匹配算法
    朴素匹配算法的原理是使用模式串 T 和目标串 S 的每一个字符都进行比较, 是一种有回溯的算法. 算法的时间复杂度无 O(m+n), m,n 分别代表目标串和模式串的长度.
    1 首先我们令 i = 0, j = 0.
    2 把 S 的第一个字符 S[0] 和 T 的第一个字符 T[0] 进行比较. (例子中 S[0] = T[0] = a)
    3 如果相同则 i, j 分别+1 . (例子中 继续比较 S[1] 和 T[1], S[1] = T[1] = b)
    4 直到出现不相同S[i] != T[j]. (例子中 S[4] = e != T[4] = d)
    5 重新比较 S 的第二个字符 S[1] 和 T 的第一个字符 T[0] , 重复执行步骤3,4直到匹配成功或所有都匹配不成功.
  • KMP 匹配算法
    在许多时候, 朴素算法的回溯增加了大量的运算时间, 特别是当模式串和主串有许多部分匹配的情况下. 因此 D. E. Kunth 与 J. H. Morris 和 V. R. Pratt对算法做出了改进, 使得算法不需要回溯, 并且可以在 O(m+n)的时间复杂度上完成串的匹配, 称为 KMP 匹配算法.
    KMP 匹配算法相对朴素匹配算法的改进之处在于, KMP 匹配算法会对匹配串本身进行一次匹配, 目的是为了当匹配串和目标串"失配"时, 确定模式串中和需重新和主串中的"失配"那一位字符进行比较的位置. 即模式串的 next[j] 函数.
    1 首先我们令 i = 0, j = 0.
    2 把 S 的第一个字符 S[0] 和 T 的第一个字符 T[0] 进行比较. (例子中 S[0] = T[0] = a)
    3 如果相同则 i, j 分别+1, 继续比较 S[1] 和 T[1]. (例子中 S[1] = T[1] = b)
    4 直到出现不相同S[i] != T[j]. (例子中 S[2] = c != T[2] = d)
    5 模式串 T 退回到 next[j] 指示位置与 S 失配位置 S[i] 进行比较. (例子中next[j] = 2, 对比S[4] = e 和 T[2] = a, 从而跳过了 S[1] - S[3] 以及 T[0] 和 T[1] 的比较.)
    6 重复3,4,5直到匹配成功或所有都匹配不成功.
  • 虽然 KMP 匹配算法无需回溯, 但是仅有当模式串和主串有许多部分匹配的情况下在时间复杂度上有明显的优势. 又因为朴素匹配模式算法在一般情况下执行时间近似 O(m+n), 因此朴素匹配模式算法仍在被大量采用.

你可能感兴趣的:(串的模式匹配算法)