数据结构(三)KMP算法

在长度为m的模式串和长度为n的主串的匹配中,如果纯用双指针暴力匹配,那么在主串上的指针i需要反复回溯,这样的时间复杂度为O(mn)。

每次匹配失败的过程中,只要j指针往后移动了,就说明此时模式串和主串的一部分是匹配成功的,这部分主串的信息我们是已知的,如果此时i不动,而让j移动到另一位置k,且满足模式串上的1~j-1与(k-j+1)~(k-1)这两部分是完全一致的,则下一次匹配可以让i不动,而使模式串直接从k开始即可,这样可以大大的减小时间复杂度。

因此,我们就明白,KMP算法主要分为两步,第一步是求j移动到另一位置k的值,我们不妨令k = next[j],即求next数组,这一部分仅仅与模式串有关,而与主串无关,时间复杂度为O(m)。

代码如下:(下标略有区别,含义一致)

//模式串p长m
for (int i = 2, j = 0; i <= m; i++)
{
    //只要匹配失败,j就不停地移到next[j]直至这一位相同,即可进行下一次匹配
    while (j != 0 && p[i] != p[j + 1])
        j = ne[j];
    if (p[i] == p[j + 1])
        j++;     // j指针往后移
    ne[i] = j; //计算next数组
}

第二步就是匹配。每次匹配失败,就令j=next[j]即可进行下一次匹配,时间复杂度为O(n)。j如果等于0了,说明此时退无可退,直接i++进行下一次匹配了。

代码如下:(下标略有区别,含义一致)

//主串s长n,模式串p长m
for (int i = 1, j = 0; i <= n; i++)
{
    //只要匹配失败,j就不停地移到next[j]直至这一位相同,即可进行下一次匹配
    while (j != 0 && s[i] != p[j + 1])
        j = ne[j];
    if (s[i] == p[j + 1])
        j++; // j指针往后移
    //移到了最后一位m-1的下一位,说明匹配成功
    if (j == m)
    {
        j = ne[j];
        //匹配成功
    }
}

题目链接:KMP字符串

AC代码如下:

#include <bits/stdc++.h>

using namespace std;

const int M = 1e5 + 10, N = 1e6 + 10;
char p[M], s[N]; // p为模式串,s为主串
int ne[M];       // next数组,ne[1]=0,ne[j]<j

int m, n;
int main()
{
    cin >> m >> p + 1 >> n >> s + 1; // p与s都习惯性地从1开始,要往后移一位

    //求next数组
    for (int i = 2, j = 0; i <= m; i++)
    {
        //只要匹配失败,j就不停地移到next[j]直至这一位相同,即可进行下一次匹配
        while (j != 0 && p[i] != p[j + 1])
            j = ne[j];
        if (p[i] == p[j + 1])
            j++;   // j指针往后移
        ne[i] = j; //计算next数组
    }

    //匹配
    for (int i = 1, j = 0; i <= n; i++)
    {
        //只要匹配失败,j就不停地移到next[j]直至这一位相同,即可进行下一次匹配
        while (j != 0 && s[i] != p[j + 1])
            j = ne[j];
        if (s[i] == p[j + 1])
            j++;    // j指针往后移
        if (j == m) //匹配成功
        {
            j = ne[j];
            cout << i - m << ' '; //打印主串上匹配成功的子串起始位置为所求
        }
    }
    cout << endl;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值