kmp理解

kmp算法的核心就是对next 数组的理解和构造,,以前一直都理解错了于是一直背kmp代码模版[其实也是很好背的orz,,今天终于纠正过来。
首先对next[i]下一个定义。
如果子串第i位与母串第j位配失败了,则子串的第next[i]位 应该与母串第j位重新匹配。即,next[i]是子串与母串j重新匹配的位点。
而这个位点有什么特性呢?子串[0~next[i]-1]与子串[i-next[i]~i-1]是完全相同的。而这个相同的串,对于第i-1位来说是最长的。
因此,子串的[0~next[i]-1]位 与母串的[j-next[i]~j-1]是相同的。只需要从next[i]起继续与母串的第j位进行比较即可。不需要再从头开始比较,这样就节省了时间。

其次是next数组的构造问题。
它其实有一个继承的思想在里面,也就是我们说的递推。
对于sz[i] 我们想要找到 它的next[i], 可以直接继承next[i-1]里信息。举个栗子:比如此时的next[i-1]=5 意味着i-1已经和前 4 个字符相匹配了,它的前一位的最长前缀为4。
而这个时候我们想知道next[i]为多少,我们需要知道以sz[i -1]结尾的最长前缀为多少,因此我们就需要比较sz[next[i-1]]与sz[i-1]是否相同,如果相同则说明,以sz[i-1]结尾的最长前缀为5 所以next[i]==5+1(next[i-1]+1);
但如果不相同呢?这就相当于子串与母串匹配失败了一样,我们需要向前移动 找到一个最近的位点与sz[i-1]重新匹配,而这个位点正是存在next[next[i-1]]里的。于是就可以乳齿不断的往前递推。直到找到与sz[i-1]相等的那个位点next[p],或者找到了子串的起始点。
最后就可以得到以sz[i-1]为末位的最长前缀 而 next[i]=next[p]+1;

总而言之,之所以觉得绕是因为,next[i]始终表示的是 以sz[i-1]为末位的最长前缀的长度的下一位。。。。。

又一次觉得复杂度是一个谜。。。不要问窝为什么是O(m+n)

写的小作文,估计只有窝自己能看懂吧qwq。。。

下面是一道裸kmp。。。。但是在VJ上T了,HDU上A了。。。。。一定是窝写的太丑。。。。
HDU 1711:http://acm.hdu.edu.cn/showproblem.php?pid=1711

//  Created by ZYD in 2015.
//  Copyright (c) 2015 ZYD. All rights reserved.
//

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <climits>
#include <string>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <set>
#include <map>
using namespace std;
#define Size 100000
#define ll long long
#define mk make_pair
#define pb push_back
#define mem(array) memset(array,0,sizeof(array))
typedef pair<int,int> P;
int n,m,t,sm[1000005],sz[10005],nxt[10005];
int kmp()
{
    int p=0;
    nxt[1]=0;nxt[2]=1;
    for(int i=3;i<=m;i++)
    {
        p=i-1;
        while(p>1 && sz[nxt[p]]!=sz[i-1])
            p=nxt[p];
        nxt[i]=nxt[p]+1;
    }
    int p1=1;int p2=1;
    while(p1<=n && p2<=m)
    {
        if(sm[p1]==sz[p2])
        {
            p1++;
            p2++;
            continue;
        }
        p2=nxt[p2];
        if(p2<1)
        {
            p1++;
            p2=1;
        }
    }
    if(p2>m) cout<<p1-p2+1<<endl;
    else cout<<-1<<endl;
    return 0;
}
int main()
{
    freopen("in.txt","r",stdin);
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        mem(sm);mem(sz);mem(nxt);
        for(int i=1;i<=n;i++)
            cin>>sm[i];
        for(int i=1;i<=m;i++)
            cin>>sz[i];
        kmp();
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值