这里是KMP的入门题集 主要运用的是next的一些简单性质 可以说是KMP裸题
在我的代码中 我使用的是整体右移法构建next
p指的是模式串
s指文本串
next 数组记录的就是下标所指字符串的最长相同前后缀
简单运用到的性质 i - next[i]就代表了最小循环节长度
第一题 友好的洛谷 (输出子串起始位置和next数组)
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e6+10;
int _next[N];
char p[N], s[N];
void Getnext(int len)
{
for(int i = 2,j = 0; i <= len; i++)
{
while( j && p[i] != p[j + 1]) j = _next[j];
if(p[i] == p[j + 1] ) j++;
_next[i] = j;
}
}
void search(int start, int lens, int lenp)
{
for(int i = start, j = 0; i <= lens; i++)
{
while( j && p[j + 1] != s[i]) j = _next[j];
if( s[i] == p[j + 1]) j++;
if(j == lenp)
{
printf("%d\n",i - lenp + 1);
}
}
}
int main()
{
scanf("%s",s + 1);
scanf("%s",p + 1);
int lenp = strlen(p + 1);
int lens = strlen(s + 1);
Getnext(lenp);
search(1, lens, lenp);
for(int i = 1; i <= lenp; i++) printf("%d ",_next[i]);//注意样例中的下标
return 0;
}
寻找文本串中模式串个数
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int nt[10010];
char p[10010];
char s[1000010];
void getnext(int lenp)
{
memset(nt, 0, sizeof nt);
for(int i = 2, j = 0; i <= lenp; i++)
{
while( j && p[i] != p[j + 1]) j = nt[j];
if(p[i] == p[j + 1]) j++;
nt[i] = j;
}
}
int search(int st, int lenp, int lens)
{
int cnt = 0;
for(int i = 1, j = 0; i <= lens; i ++)
{
while( j && s[i] != p[j + 1]) j = nt[j];
if(p[j + 1] == s[i]) j++;
if(j == lenp)
{
j = nt[j];
cnt ++;
}
}
return cnt;
}
int main()
{
int n;
scanf("%d",&n);
while(n--)
{
scanf("%s",p + 1);
scanf("%s",s + 1);
int lenp = strlen(p + 1);
int lens = strlen(s + 1);
getnext(lenp);
int ans = search(1, lenp, lens);
printf("%d\n",ans);
}
return 0;
}
求文本串中循环节个数
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
char p[1000010];
int nt[1000010];
void getnext(int lenp)
{
memset(nt, 0, sizeof nt);
for(int i = 2, j = 0; i <= lenp; i++)
{
while( j && p[i] != p[j + 1]) j = nt[j];
if(p[i] == p[j + 1]) j++;
nt[i] = j;
}
}
int main()
{
while(1)
{
memset(p, 0, sizeof p);
scanf("%s", p + 1);
if(p[1] == '.') break;
int lenp = strlen(p + 1);
// cout << lenp << endl;
getnext(lenp);
int k = lenp - nt[lenp];
// printf("%d\n",k);
if(lenp % k == 0) printf("%d\n",lenp / k);
else printf("-1\n");
}
return 0;
}
给一个字符串,问你前缀和后缀相同的位置有哪些
2752 -- Seek the Name, Seek the Fame
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 4e5 + 10;
char p[N];
int nt[N], ans[N];
void getnext(int lenp)
{
for(int i = 2, j = 0; i <= lenp; i++)
{
while( j && p[i] != p[j + 1]) j = nt[j];
if(p[i] == p[j + 1]) j++;
nt[i] = j;
}
}
void output(int len)
{
for(int i = 0; i <= len; i++) cout << p[i] << " ";
cout << endl;
for(int i = 0; i <= len; i++) cout << nt[i] << " ";
cout << endl;
}
int main()
{
while(~scanf("%s", p + 1))
{
int lenp = strlen(p + 1), cnt = 0;
getnext(lenp);
// output(lenp);
int last = nt[lenp];
while( last != 0 )
{
ans[cnt++] = last;
last = nt[last];
}
for(int i = cnt - 1; i >= 0; i--)
{
printf("%d ", ans[i]);
}
printf("%d\n",lenp);
}
return 0;
}
求出所有前缀中的循环节
141. 周期 - AcWing题库
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
const int N = 1e6+10;
int nt[N];
int n, cnt;
char p[N];
void Getnext(int lenp)
{
memset(nt, 0, sizeof nt);
int j = 0;
for(int i = 2; i <= lenp; i++)//从2开始
{
while( j && p[i] != p[j + 1]) j = nt[j];
if(p[i] == p[j + 1]) j++;
nt[i] = j;
}
}
int main()
{
while(1)
{
cnt++;
int lenp;
if(cnt != 1) puts("");
scanf("%d",&lenp);
if( lenp == 0) break;
scanf("%s",p + 1);
//Get next[
Getnext(lenp + 1);
printf("Test case #%d\n",cnt);
for(int i = 1; i <= lenp; i++)
{
int t = i - nt[i];
if( i % t == 0 && i / t > 1)
{
printf("%d %d\n", i, i / t);
}
}
}
return 0;
}