版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/junxing2018_wu/article/details/108210839
有一个文本串T, 和一个模式串P, 现在要查找P在T中的位置, 怎么查找呢?
本文参数说明
- target: 文本串
- pattern: 模式串
- i: 文本串target索引指针
- j: 模式串pattern索引指针
[worst case] - 暴力匹配
- 简单粗暴
- 基本思路:
- 逐个比较
匹配成功时,i 右移一个位置(i=i+1),j 右移一个位置(j=j+1),继续比较…
匹配失败时,i 右移一个位置(i=i-j+1),j 回到模式串起点(j=0),继续比较… - 直到 j 遍历完 pattern(匹配成功), 或者 i 遍历完 target (匹配失败)
def violent_match(target: str, pattern: str): """ 暴力匹配 """ len_t, len_p = len(target), len(pattern) i, j = 0, 0 while i < len_t and j < len_p: if target[i] == pattern[j]: # 如果当前字符匹配成功(即t[i] == p[j]), 则i++, j++ i += 1 j += 1 else: # 如果失配(即t[i] != p[j]), 令i = i - (j - 1), j = 0 i -= j - 1 j = 0 # 匹配成功, 返回模式串p在文本串t中的位置, 否则返回-1 if j == len_p: return i - len_p else: return -1
- 逐个比较
[better case] - kmp 模式串匹配
- 利用已经匹配过的信息,达到跳跃式匹配
- 保持 i 指针不回溯, 只通过修改 j 指针, 让模式串尽量地移动到有效的位置
- 基本思路:
- 预先扫描模式串 pattern 生成 next 跳转表,指导 j 指针跳转(重难点)
- 逐个比较
匹配成功时,i 右移一个位置(i=i+1),j 右移一个位置(j=j+1),继续比较…
匹配失败时,根据 next 跳转表决定 j 指针(下一次从 pattern 哪个位置开始比较) - 直到 j 遍历完 pattern(匹配成功), 或者 i 遍历完 target (匹配失败)
- next跳转表 生成规则:
-
简单说:只需求模式串 j 指针前面的字符串里前缀与后缀相同的最大长度 k
理由:pattern[0 ~ k-1] == pattern [j-k ~ j-1] 等价于 此时 pattern 前 k 位已满足文本串
比如:abcacb >>> [0, 0, 0, 0, 1, 0] -
特殊处理:
- next 跳转表 首位为 -1。指针 j 已经在最左边了, 不可能再移动了, 这时候要 i 指针后移。
故:abcacb >>> [-1, 0, 0, 0, 1, 0] - 避免出现 pattern[j] = pattern[ next[j] ],此时需要递归, k = pnext[k]
比如:abab ==> [ -1, 0, 0, 1 ] 改成 [-1, 0, -1, 0]
- next 跳转表 首位为 -1。指针 j 已经在最左边了, 不可能再移动了, 这时候要 i 指针后移。
def kmp_match(target: str, pattern: str): """ kmp 模式串匹配 基本思路: 利用已经部分匹配这个有效信息, 保持i指针不回溯, 通过修改j指针, 让模式串尽量地移动到有效的位置 每次匹配失败了, 根据next数组决定下一次从哪个位置开始匹配. 从而达到跳跃式匹配 """ python def gen_next(pattern: str): """ 生成指定模式串的next跳转表(循环) 基本思路: 决定每次匹配失败时, 直接从哪个位置开始比较 需要提前扫描模式串, 预先知道模式串的前缀与后缀相同的最大长度 next 每一个元素 = 该元素之前的子串前缀与后缀相同的最大长度 P[0, k-1] == P[j-k, j-1] 即 next[j] = k """ # 模式串长度 len_p = len(pattern) # 预设next跳转表都为0 (模式串没有相同子序列) pnext = [0 for i in range(len_p)] # j 已经在最左边了, 不可能再移动了, 这时候要 i 指针后移. # 所以 next 首位为 -1, pnext[0] = -1 k, j = -
-