kmp-模版及两道模版题

kmp这东西还是蛮强的,能在线性的时间完成字符串匹配,相比wo我之前会的都是o(n2)的 快的不是一点半点,下面简单说下kmp的原理

现在我们有母串s,模式串t;

我们传统的字符串匹配都思想是,不管此次匹配成功与否,都是模式串向前移动一位继续匹配,这样子效率肯定是很慢的,因为我们根本没有利用到之前已经匹配过的信息

kmp算法就是利用之前匹配的信息,使模式串前移更多的位,使其效率大大提升,那么我们怎么知道什么时候前移多少位呢,首先我们假设现在模式串第j位与母串第i位匹配成功

,接下来就是匹配s[i+1]和t[j+1]如果s[i+1]!=t[j+1]那么按照我们传统的匹配方法我们这时j变为0,从头继续开始匹配,kmp,改进的就是这里,实际上不用,只用找到,前面匹配过的前缀等于后缀的最大长度即可,那么我们怎找到这个前缀等于后缀的最大长度呢(其实我觉的这个才是kmp最难的)我们可以通过预处理的方法求出,开一个next数组表长度

为i的字符串的前缀等于后缀的最大长度是多少,为什么要这样定义呢,因为字符串下标是从0开始的,这样的话每次找到最大长度d后那么t【d】代表的是最大长度后面的字符,就很方便,然后我们怎么去找出每个位置的最大长度呢,我们可以发现下一位的最大长度只跟上一次的有关,那么我们可以用一个变量记录上一次的的最大长度,然后跟第i位比较就行了(这里说的有点不清楚,请看代码);

参考博文:http://www.cnblogs.com/kuangbin/archive/2012/08/14/2638803.html 

 http://www.matrix67.com/blog/archives/115 

kmp模版:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#include<vector>
#include<iostream>
#include<sstream>
#define LL long long
using namespace std;
const int maxn  = 1e5;
char s[maxn];
char t[maxn];
int lens;
int lent;
int next[maxn];
void getNext()
{
    int  j =  0 ;
    int k = -1;
    next[0] = -1;
    while(j<lent)
    {
        if(k==-1||t[j]==t[k])
        {
            next[++j] = ++k;
        }
        else
            k = next[k];
    }
}
int kmp()
{
    getNext();
    int ans = 0;
    int k = 0;
    for(int i = 0 ; i<lens;i++)
    {
        while(k>0&&s[i]!=t[k])
        {
            k = next[k];
        }
        if(s[i] == t[k])
        {
            k++;
        }
        if(k == lent)
        {
            ans++;
        }
    }
    return ans;
}
int main()
{
    while(~scanf("%s%s",s,t))
    {
        lens = strlen(s);
        lent = strlen(t);
        cout<<kmp()<<endl;
    }
}
下面是两到模版提:

poj3461

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#include<iostream>
#include<sstream>
using namespace std;
const int maxn = 1e6+50;
char s[maxn];
char t[maxn];
int Next[maxn];
int n,m;
int lens;
int lent;
int getNext()
{
    int j=0;
    int k = -1;
    Next[0] = -1;
    while(j<lent)
    {
        if(k==-1||t[j]==t[k])
        {
            Next[++j] = ++k;
        }
        else
            k = Next[k];
    }
    return 0;
}
int kmp()
{
    int k = 0;
    getNext();
    int ok = 0;
    int ans = 0;
    for(int i = 0 ;i<lens;i++)
    {
        while(k>0&&s[i]!=t[k])
        {
            k = Next[k];
        }
        if(s[i] == t[k])
        {
            k++;
        }
        if(k==lent)
        {
            ans++;
        }
    }
   return ans;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",t);
        scanf("%s",s);
        lens = strlen(s);
        lent = strlen(t);
        printf("%d\n",kmp());
    }
}

hdu1711

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#include<iostream>
#include<sstream>
using namespace std;
const int maxn = 1e6+50;
int a[maxn];
int b[maxn];
int Next[maxn];
int n,m;
int getNext()
{
    int j=0;
    int k = -1;
    Next[0] = -1;
    while(j<m)
    {
        if(k==-1||b[j]==b[k])
        {
            Next[++j] = ++k;
        }
        else
            k = Next[k];
    }
    return 0;
}
int kmp()
{
    int k = 0;
    getNext();
    int ok = 0;
    for(int i = 0 ;i<n;i++)
    {
        while(k>0&&a[i]!=b[k])
        {
            k = Next[k];
        }
        if(a[i] == b[k])
        {
            k++;
        }
        if(k==m)
        {
            cout<<i+1-m+1<<endl;
            ok = 1;
            return 0;
        }
    }
    if(!ok)
        puts("-1");
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {scanf("%d%d",&n,&m);
        for(int i = 0 ;i<n;i++)
        {
            scanf("%d",&a[i]);
        }
        for(int i = 0 ;i<m;i++)
        {
            scanf("%d",&b[i]);
        }
        if(n<m)
            puts("-1");
        else
        {
            kmp();
        }
    }
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值