···············································这是一个叫前言的东西·············································
很久以前就知道有一个叫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