字符串比较——KMP
本文主要内容:
- 基础复习
- KMP求字符串最小循环节
1 基础复习
- 主要功能
- 字符串匹配
- 求字符串最小循环节
- 时间复杂度
o(n)- 基本思路:
1.不用kmp算法只要文本串S(长度为n)与搜索串P(长度为m)对应的某一个字符不同,P就往后滑动1位再从头开始比较,用了kmp算法P滑动k位(k>=1 && k < m)从原位置进行比较
- 用一个next[]数组记录P的所有位置的最长匹配前缀(前缀等于后缀的最长前缀)长度,当比较S和P时只要有一个字符不同,P进行滑动(j=next[j]),使已经P和S已经匹配的那段中P的最长匹配前缀与S的后缀对齐,继续进行比较
- 构建next[]与比较S和P是否相等的过程相似,区别在于构建next[]比较的是P的后缀和P的前缀是否相等
- 注意事项:
- j指向的是已经匹配的p的末尾,待匹配的是P[j+1],因为next[j]维护的是以处的最长匹配前缀长度。
模板题:Acwing831.KMP字符串
#include<iostream>
using namespace std;
const int N = 1e6 + 5;
char p[N], s[N];
int n, m, ne[N];
int main()
{
cin >> n >> p + 1 >> m >> s + 1;
for(int i = 2, j = 0; i <= n; i++)
{
while(j && p[j + 1] != p[i])j = ne[j];
if(p[i] == p[j + 1])j++;
ne[i] = j;
}
for(int i = 1, j = 0; i <= m; i++)
{
while(j && s[i] != p[j + 1])j = ne[j];
if(s[i] == p[j + 1])j++;
if(j == n)
{
cout << i - n << " ";
j = ne[j];
}
}
return 0;
}
2 KMP求字符串最小循环节
例题:Acwing141. 周期
- 思路:
- 任意一段字符的最小循环节长度等于i-next[i]
- 构建字符串的next[],遍历一遍该字符串即可求得具有循环节的前缀串的位置和循环节数量
#include<iostream>
using namespace std;
const int N = 1e6 + 5;
int ne[N];
char str[N];
int n;
void get_next()
{
for(int i = 2, j = 0; i <= n; i++)
{
while(j && str[j + 1] != str[i])j = ne[j];
if(str[j + 1] == str[i])j++;
ne[i] = j;
}
}
int main()
{
int T = 0;
while(scanf("%d", &n), n)
{
scanf("%s", str + 1);
get_next();
printf("Test case #%d\n", ++T);
for(int i = 1; i <= n; i++)
{
int t = i - ne[i]; //t为前缀最小循环节
if(t != i && i % t == 0)
printf("%d %d\n", i, i / t);
}
printf("\n");
}
return 0;
}