···············································这是一个叫前言的东西·············································
很久以前就知道有一个叫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;
}
- hihocoder 1084 : 扩展KMP
还没有做……
五、总结未做题
—— CYCKCN