kmp and exkmp(总结于wls的b站教学视频)

1.解决什么问题

         kmp解决在字符串s中查找某个字符串p是否出现,也就是字符串匹配问题。

2.解决方法

        假设s的长度为n,p的长度为m。

2.1 暴力匹配

        枚举s中每个长度为m的子串,然后一位一位的比较这些子串和p一不一样。总的时间复杂度为O(nm)。

2.2 哈希

        先预处理出字符串p、s的哈希值,再判断s中每个长度为m的子串的哈希值和p是否一样。但很有可能会发生哈希冲突。总的时间复杂度为O(n+m)。

2.3 KMP

简介

        在KMP算法中,对于s中每个位置i,我们要找到最大的 j 满足s[i - j + 1]...s[i] 和 p[1]...p[j]是相同的,既在s中以s[i]结尾的后缀和p中以p[j]结尾的前缀是相同的。

 i 向后移有两种情况:

1. j 不等于 m,s[ i + 1 ] 和 p[ j + 1 ]相同。这种情况 j 也向右移动一位

2. s[ i + 1 ] 和 p[ j + 1 ]不同或 j 等于 m 。这种情况只要让 j 向前回到满足串 s[ i - k + 1 ]…s[ i ] 和 p[ 1 ]… p[ k ]完全相等,且k的值最大的位置,然后继续判断。如果s[ i + 1 ] 和 p[ j + 1 ] 仍然不相同,那就继续向前回退,知道s[ i + 1 ] 和 p[ j + 1 ] 相同或 j = 0。

为什么要找k最大的呢?

        满足条件的 k 可能有很多种,然而对于一个固定的 i ,要使得 j 最大,j 需要回退到 k 的位置

,所以 k 也需要最大。

如何快速求k?

        k 其实只和 p 有关,前面提过 s[ i - k + 1 ] ... s[ i ] 和 p[ 1 ] ... p[ j ] 相同。所以我们要求的是最大的 k 满足 k < j ,使得p[ 1 ] … p[ k ]和 p[ j - k + 1].… p[ i ]完全相同。也就是p当中长度为 k 的前缀和 p 当中以 p[ j ] 为结尾,长度为 k 的后缀相同。

用 next 数组来维护每个 j 对应的 k 

 假设第 j 位的 next[ j ] = k 已知,要求第 j + 1 位的next[ j + 1 ],分为两种情况:

1. p[ k + 1 ] 和 p[ j + 1 ] 一样,那就再往后匹配一位,next[ j + 1 ] = k + 1;

2. p[ k + 1 ] 和 p[ j + 1 ] 不一样,那就回退到 next[ j ],如果下一位还不一样,还是继续回退。直到相同或者 j 等于0.

代码模板:

// s[]是长文本,p[]是模式串,n是s的长度,m是p的长度
/*求模式串的Next数组:*/
for (int i = 2, j = 0; i <= m; i ++ )
{
    while (j && p[i] != p[j + 1]) j = ne[j];
    if (p[i] == p[j + 1]) j ++ ;
    ne[i] = j;
}

// 匹配
for (int i = 1, j = 0; i <= n; i ++ )
{
    while (j && s[i] != p[j + 1]) j = ne[j];
    if (s[i] == p[j + 1]) j ++ ;
    if (j == m)
    {
        j = ne[j];
        // 匹配成功后的逻辑
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值