KMP

···············································这是一个叫前言的东西·············································
很久以前就知道有一个叫KMP(看毛片)的恶心算法了,但在学之前真心不知道这么恶心直到……(牛犇不要吐槽蒟蒻)
下面是一个自己对KMP的小总结,学得不久,可能不全面
············································这是前言和正文的分界线··············································

一、一点和字符串有关的东西
······说KMP之前先来看看字符串吧……
先来个例子:S = abbabbb
1、子串:串中任意个连续的字符组成的子序列称为该串的子串(完全可以理解为数学中的子集);
如:S的子串有ab abb abbb ……等;
2、前缀:包括串中首字母的子串;
如:S的前缀有a ab abb abba abbab abbabb abbabbb;
3、后缀:包括串中尾字符的子串;
如:S的后缀有b bb bbb abbb babbb bbabbb abbabbb;

二、NEXT数组
1、······铺垫了那么多看个题目先……
POJ 2752 Seek the Name, Seek the Fame给定一字符串S,求S中既是前缀又是后缀的子串。 ——我还没有写就不放代码了,next数组就可以了······似乎
*既是前缀又是后缀:即出现在前缀列又出现在后缀列的子串;
如:S = abbabbb 中 ab就是既是前缀又是后缀
2、next数组
对于字符串T,next[i]表示对于前缀S1…i 这个字符串,最长的一个与其等长前缀相等的后缀的长度(完全相同的(长度相等,每一位都相同)前缀、后缀的最大长度 (不考虑字符串本身))。
如:字符串 abbabbb中
next[5]:
abbab 前缀:a ab abb abba
后缀:b ab bab bbab
即为ab长度–> next[5] = 2;
3、代码实现
这里写图片描述

①nxt[1] = 0 ;
for (int i = 2; i <= len; ++i)//t[i]为字符串第i位字符,nxt即为next数组/
{
int j = nxt[i - 1];
while(j > 0 && t[i] != t[j + 1]) j = nxt[j];
if (t[i] == t[j + 1]) j++;
nxt[i] = j;
}
②void next(string T, int &next[])
{
int i = 1; next[1] = 0;j = 0;
while (i < T[0]){
if (j == 0 || T[i] == T[j]){
++i;++j;
if (T[i] !=  T[j])    next[i] = j;
else next[i] = next[j]; 
}
else j = next[j];
}
}

三、KMP算法
1、上题目 POJ 3461 Oulipo给定字符串S和T,求T在S中的出现次数。
这里写图片描述
2、KMP代码

while(p<=lens){
  if(s[p]==t[j]){
  ++p,++j;
  if(j>lent){
  puti(p-j+1);
  putchar('\n');
  if(j!=1)j=ne[j-1]+1;
  else p++;
  }
  }
  else{
  if(j!=1)j=ne[j-1]+1;
  else p++;
  }
}  

3、AK大神YWJ的KMP算法

int kmp()
{
  int ans=0,j=0;
  for(int i=1; i<=l1; i++)
  {
  while(j&&b[j+1]!=a[i]) j=nxt[j];
  if(b[j+1]==a[i]) j++;
  if(j>=l2) ans++,j=nxt[j];
  }
  return ans;
}

4、再放一种KMP函数(有注释)

int Index(SString S, SString T, int pos){
/返回子串T在主串S中第pos个字符之后的位置。若不存在,则函数值为0。
其中,T非空,1 <= pos <= StrLength(S)。/
i = pos;j = 1;
while (i <= S[0] && j <= T[0]){
if (S[i] == T[j]){
++i;++j;
}//继续比较后继字符/
else {
i = i - j + 2;j = 1;
}// 指针后退重新开始匹配 /
} 
if (j > T[0])return i - T[0];
else return 0;
}//Index/

四、KMP的练习

  • hihoCoder 1015
    • 一道简单的KMP裸题
    • 然后是对自己的一点小提醒
      1、next数组对应的是模式串
      2、注意模式串和原串都要从下标’1’开始
      3、本题的ans记录的是模式串出现的次数,可根据需要更改
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;

int n;
char a[1000100], b[10100];
int nxt[1000100];
int la, lb;

int main(){
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i){
        cin >> b >> a;
        la = strlen(a), lb = strlen(b);
        for (int j = lb; j > 0; j--) b[j] = b[j - 1];
        for (int j = la; j > 0; j--) a[j] = a[j - 1];
        nxt[1] = 0;
        for (int j = 2; j <= lb; ++j){
            int p =nxt[j - 1];
            while (p > 0 && b[p + 1] != b[j]) p = nxt[p];
            if(b[p + 1] == b[j]) p++;
            nxt[j] = p;
        }
        int ans = 0, p = 0;
        for (int j = 1; j <= la; ++j){
            while (p && b[p + 1] != a[j]) p = nxt[p];
            if (b[p + 1] == a[j]) p++;
            if (p >= lb) ans++, p = nxt[p];
        }
        printf("%d\n", ans);
        memset(a, '0', sizeof(a));
        memset(b, '0', sizeof(b));
    }
    return 0;
}
  • CJOJ P2566 字符串最大值
    • 一个对next数组使用的题目,挺简单的
    • 一点点提示和反省
      1、如abababa中,next[7]的值是由 ababa ba 及 ab ababa决定的!!!(一下午都把next数组当成回文数了……)
      2、这道题比对时应倒着来(手玩一下吧)
      3、最后别忘了乘以长度
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;

char t[1000100];
int nxt[1000100];
int ans[1000100];
int maxx = 0;

int main(){
    scanf("%s", t + 1);
    nxt[1] = 0;
    int len = strlen(t + 1);
    for (int i = 2; i <= len; i++){
        int j = nxt[i - 1];
        while (j && t[j + 1] != t[i]) j = nxt[j];
        if (t[j + 1] == t[i]) nxt[i] = j + 1;
    }
    for (int i = len; i >= 1; --i) ans[i]++, ans[nxt[i]] += ans[i];
    for (int i = 1; i <= len; ++i) maxx = max(maxx, ans[i] * i);
    printf("%d\n", maxx);
    return 0;
}

五、总结未做题

  1. hihocoder 1084 : 扩展KMP
  2. POJ 2752 Seek the Name, Seek the Fame
  3. POJ 3461 Oulipo

—— CYCKCN

我们这里说的KMP不是拿来放电影的(虽然我很喜欢这个软件),而是一种算法。KMP算法是拿来处理字符串匹配的。换句话说,给你两个字符串,你需要回答,B串是否是A串的子串(A串是否包含B串)。比如,字符串A="I'm matrix67",字符串B="matrix",我们就说B是A的子串。你可以委婉地问你的MM:“假如你要向你喜欢的人表白的话,我的名字是你的告白语中的子串吗?” 解决这类问题,通常我们的方法是枚举从A串的什么位置起开始与B匹配,然后验证是否匹配。假如A串长度为n,B串长度为m,那么这种方法的复杂度是O (mn)的。虽然很多时候复杂度达不到mn(验证时只看头一两个字母就发现不匹配了),但我们有许多“最坏情况”,比如,A= "aaaaaaaaaaaaaaaaaaaaaaaaaab",B="aaaaaaaab"。我们将介绍的是一种最坏情况下O(n)的算法(这里假设 m<=n),即传说中的KMP算法。 之所以叫做KMP,是因为这个算法是由Knuth、Morris、Pratt三个提出来的,取了这三个人的名字的头一个字母。这时,或许你突然明白了AVL 树为什么叫AVL,或者Bellman-Ford为什么中间是一杠不是一个点。有时一个东西有七八个人研究过,那怎么命名呢?通常这个东西干脆就不用人名字命名了,免得发生争议,比如“3x+1问题”。扯远了。 个人认为KMP是最没有必要讲的东西,因为这个东西网上能找到很多资料。但网上的讲法基本上都涉及到“移动(shift)”、“Next函数”等概念,这非常容易产生误解(至少一年半前我看这些资料学习KMP时就没搞清楚)。在这里,我换一种方法来解释KMP算法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值