c++ kmp算法字符匹配_算法(一): 字符串匹配

小锅炒的数据结构与算法学习系列之算法,本节主要简单了解字符串匹配算法。

1.字符串匹配

对于字符串对象,最重要的操作之一是字符串匹配。这个操作不仅本身很重要,还是许多其他字符串操作的基础。字符串匹配也被称为字串匹配(string matching)或者符串查找(string searching)。有些书里称为模式匹配(pattern matching)而模式匹配是一个内涵更广的概念。假设有两个字符串:

字符串匹配就是在字符串t中查找与字符串p相同的子串的操作。字符串t称为目标串字符串p模式串。通常有m<,也就是说,模式串的长度远小于目标串的长度。如果从标串的某个位置j开始,模式串里的每个字符都与目标串里的对应字符相同,就是找到一个配对。如果在比较中遇到了一对不同的字符,那就是不匹配,说明模式串不能与目标串中从位置j开始的子串匹配。

2. 字符串朴素匹配算法

朴素的字符串匹配算法(Naive String Matching Algorithm),就是穷举法,枚举法,也叫暴力匹配。其基本流程如下:

1. 从目标的第j个字符开始,同模式串的第1个字符比较。
2. 如果相同,目标和模式串均指向下一个字符继续比较
3. 如果不相同,目标串指针指向初始字符的下一个字符,
   模式串指针返回第一个字符位置。

489c8b52c690b91266dd771bb88c80b8.gif

2.1 朴素算法实现
def navie_matching(t, p):
    m, n = len(p), len(t)
    i, j = 0, 0
    while i and
 j          if p[i] == t[j]:
            i +=  1
            j +=  1 else:
            i, j =  0, j - i +  1 if i == m: return j - i return  -1

3.KMP 算法

朴素字符串算法,寻找匹配的字符子串时,当中间某个字符不匹配时,目标串相对原始位置只移动一个字符,前面匹配正确的字符信息完全没有使用到。KMP算法考虑到这一点,对模式串进行编码,利用了模式自身的特性提高了搜索匹配的效率。KMP算法大致思想:当目标字符和模式字符不匹配时,目标串指针不改变模式串指针返回到next[i]的位置,i为模式串的第i+1个字符的下标。next数组详见3.2。

dd6accb3a85b075a65289db0098571e1.png

3.1 KMP算法实现
def matching_KMP(t, p, pnext):
    j, i = 0, 0
    n, m = len(t), len(p)
    while i and j         if i == -1 or t[j] == p[i]:
            j += 1
            i += 1
        else:
            i = pnext[i]
    if i == m:
        return j - i
    return -1

pnext 即为next数组,详细见3.2

def gen_pnext(p):
    i, k, m = 0, -1, len(p)
    pnext = [-1] * m
    while i -1:        if k == -1 or p[i] == p[k]:            i, k = i+1, k+1            pnext[i] = k        else:            k = pnext[k]    return pnext
3.2 next数组
  • 1.什么是next数组字符串前缀:字符串除最后一个字符外的全部头部集合。字符串后缀:除字符串除第一个字符外的全部尾部集合。next数组: 除当前字符外的最长相同前缀后缀的长度。例如,字符串p = abcac的前缀为{a,ab,abc,abca},后缀{c,ac,cac,bcac}
模式串abcac
max(len)00010
next-10001
  • 2.next数组的作用如图所示,p[i] != t[j]时,目标串指针保持不动,模式串指针i=next[i]。所以next数组的作用很明显是记录模式串指针的回溯位置,换一句话说就是next数组隐含了模式指针应该移动的距离:i-next[i]-1。

    f3555ffd2e3a432f0e38ae70e6211fb1.png

  • 3.next数组的生成采用动态规划的思想
初始化:next[0] = -1, next[1] = 0
已 知:next[0], ...,next[i-1]
且next[i-1] = k, 求:next[i]
if p[i] = p[k],则 next[i] = k+1, 
if p[i] != p[k], 指针 k = next[k], 继续比较。

为何递归前缀指针 k = next[k]?根据next数组的含义,next存储的值为最长相同前后缀的长度。

e9194202140ac9ddd4582a7ca489642c.png

如果p[k] != p[i],将指针移动到next[k]即具有最长相同前缀和后缀的长度如果p[i] = p[next[k]],则 next[i] = next[k] + 1, 如果p[i] != p[next[k]],指针继续移动到上一个最长前后缀相等的位置,即k = next[next[k]]。next数组的生成函数如下:

def gen_pnext(p):
    i, k, m = 0, -1, len(p)
    pnext = [-1] * m
    while i -1:
        if k == -1 or p[i] == p[k]:
            i, k = i+1, k+1
            pnext[i] = k
        else:
            k = pnext[k]
    return pnext
  • 4.为什么KMP高效朴素匹配算法时,当遇到错误匹配时目标串指针要返回到j-i+1的位置,而KMP算法目标串指针不需要回溯,仍然在当前位置。

    f3555ffd2e3a432f0e38ae70e6211fb1.png

说明:

KMP算法的精髓就是开发了一套分析和记录模式串信息的机制(和算法)(next数组!!!),而后借助得到的信息加速匹配。KMP最大的难点在于next数组的理解。

上述next数组表述还存在不少瑕疵。

参考:

1.数据结构与算法:Python语言描述 (裘宗燕) 

2.https://www.cnblogs.com/zhangtianq/p/5839909.html

3.https://baijiahao.baidu.com/s?id=1659735837100760934&wfr=spider&for=pc

4.https://blog.csdn.net/dark_cy/article/details/88698736

- END -
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值