算法讲解: KMP算法最浅显易懂
模板来源: 从头到尾彻底理解KMP
例题:2018 UESTC Training for Search Algorithm & String——L主楼
题意:求字符串中的最短循环节,并输出该循环节
KMP最小循环节、循环周期:
定理:假设S的长度为len,则S存在最小循环节,循环节的长度L为len-next[len],子串为S[0…len-next[len]-1]。
(1)如果len可以被len - next[len]整除,则表明字符串S可以完全由循环节循环组成,循环周期T=len/L。
(2)如果不能,说明还需要再添加几个字母才能补全。需要补的个数是循环个数L-len%L=L-(len-L)%L=L-next[len]%L,L=len-next[len]。
注意这里的len是原字符串的len+1;
#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
int T, len;
char s[100000 + 5];
int nex[100000 + 5];
void get_next(char* p, int next[]){
int pLen = strlen(p) + 1;//要求循环节长度时这里才+1
next[0] = -1;
int k = -1;
int j = 0;
while (j < pLen - 1){
if (k == -1 || p[j] == p[k]){
++j;++k;
if (p[j] != p[k])
next[j] = k;
else
next[j] = next[k];
}
else{
k = next[k];
}
}
}
int kmp(char* s, char* p){
int i = 0;
int j = 0;
int sLen = strlen(s);
int pLen = strlen(p);
while (i < sLen && j < pLen){
//①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++
if (j == -1 || s[i] == p[j]){
i++;
j++;
}
else{
//②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]
//next[j]即为j所对应的next值
j = nex[j];
}
}
if (j == pLen)
return i - j;
else
return -1;
}
int main() {
scanf("%d", &T);
while (T--) {
memset(nex, 0, sizeof(nex));
scanf("%d", &len);
scanf("%s", s);
len = strlen(s);
get_next(s, nex);
printf("%d\n", len - nex[len]);
for (int i = 0; i < len - nex[len]; i++)
printf("%c", s[i]);
printf("\n");
}
return 0;
}
HDU1711(数字KMP)
#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
int T, len;
int len1, len2;
int s1[1000000 + 5];
int s2[1000000 + 5];
int nex[1000000 + 5];
void get_next(int* p, int next[]) {
int pLen = len2;//要求循环节长度时这里才+1
next[0] = -1;
int k = -1;
int j = 0;
while (j < pLen - 1) {
if (k == -1 || p[j] == p[k]) {
++j; ++k;
if (p[j] != p[k])
next[j] = k;
else
next[j] = next[k];
}
else {
k = next[k];
}
}
}
int kmp(int* s, int* p) {
int i = 0;
int j = 0;
int sLen = len1;
int pLen = len2;
get_next(s2, nex);
while (i < sLen && j < pLen) {
//①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++
if (j == -1 || s[i] == p[j]) {
i++;
j++;
}
else {
//②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]
//next[j]即为j所对应的next值
j = nex[j];
}
}
if (j == pLen)
return i - j;
else
return -1;
}
int main() {
scanf("%d", &T);
while (T--) {
scanf("%d %d", &len1, &len2);
for (int i = 0; i < len1; i++)
scanf("%d", &s1[i]);
for (int i = 0; i < len2; i++)
scanf("%d", &s2[i]);
if (kmp(s1,s2) == -1)
printf("-1\n");
else
printf("%d\n", kmp(s1,s2)+1);
}
return 0;
}