16 | KMP 算法的典型应用(匹配位置、求出所有相同前缀后缀、最多字串重复次数)
记忆两段代码(注意字符串从下标为 1 开始存储)
- 求 解 next 数组的代码
// 计算字符串 p 的next 值
for(int i=1,j=0;i<lenp;i++){
while(j&&p[i+1]!=p[j+1]) j=ne[j];
if(p[i+1]==p[j+1]) j++;
ne[i+1]=j;
}
- 子串 匹配 代码
for(int i=0,j=0;i<lens;i++){
while(j&&s[i+1]!=p[j+1]) j=ne[j];
if(s[i+1]==p[j+1]) j++;
//判断匹配成功、
if(j==lenp){
//
}
}
🔧 字串位置
题目描述
给定一个父字符串s和子字符串p,请按照从前向后的顺序,请求出p在s中所有出现的起始位置。
例如:s = "ABADABCEABABA",p = "ABA",则求解的结果是:1 9 11。
输入
第1行读入一个仅包含大写字母的字符串s;
第2行读入一个仅包含大写字母的字符串p;
s和p均是长度不超过106的字符串。
输出
输出1行,按题意输出p在s中出现的位置,数字之间用空格隔开。
样例
输入
ABADABCEABABA
ABA
输出
1 9 11
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 +10;
char s[N],p[N];
int ne[N]; // ne[j] 代表 p 字符串 [1,j] 这个字串前缀和后缀相等的最长长度
int main(){
scanf("%s %s",s+1,p+1);
// 计算 next 数组的值
ne[0]=ne[1]=0;
int lens= strlen(s+1),lenp=strlen(p+1);
// 计算字符串 p 的next 值
for(int i=1,j=0;i<lenp;i++){
while(j&&p[i+1]!=p[j+1]) j=ne[j];
if(p[i+1]==p[j+1]) j++;
ne[i+1]=j;
}
// kmp 匹配
for(int i=0,j=0;i<lens;i++){
while(j&&s[i+1]!=p[j+1]) j=ne[j];
if(s[i+1]==p[j+1]) j++;
//判断匹配成功、
if(j==lenp){
printf("%d ",i+1-lenp+1); // 这个时候 i 还没有 +1 ,草稿纸画一下
}
}
return 0;
}
🔧最多字串重复次数
题目描述
给定若干个长度 ≤106 的字符串,询问每个字符串最多是由多少个相同的子字符串重复连接而成的。如:ababab 则最多有 3 个 ab 连接而成。
输入
输入若干行(所有行的字符串的长度之和≤107),每行有一个字符串,字符串仅含英语小写字母。特别的,字符串可能为 . 即一个半角句号,此时输入结束。
输出
对于每行输入,输出一个整数,代表计算的结果。
样例
输入
abcd
aaaa
ababab
.
输出
1
4
3
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
char s[N];
int ne[N];
int main(){
while(scanf("%s",s+1) && s[1] != '.'){
//计算字符串s的next数组
ne[0] = ne[1] = 0;
int len = strlen(s+1);
//重新求解 ne 数组
for(int i = 1,j = 0;i < len;i++){
while(j && s[i+1] != s[j+1]) j = ne[j];
if(s[i+1] == s[j+1]) j++;
ne[i+1] = j;
}
//判断
if(len % (len - ne[len])== 0) printf("%d\n" ,len / (len-ne[len]));
else printf("%d\n",1);
}
return 0;
}
🔧 找到所有相等的前缀和后缀
题目描述
给定若干由小写字母组成的字符串(这些字符串总长 ≤4×105),在每个字符串中求出所有既是前缀又是后缀的子串长度。
例如:ababcababababcabab,既是前缀又是后缀的:ab,abab,ababcabab,ababcababababcabab。
输入
输入若干行,每行一个字符串。
输出
对于每个字符串,输出一行,包含若干个递增的整数,表示所有既是前缀又是后缀的子串长度。
样例
输入
ababcababababcabab
aaaaa
输出
2 4 9 18
1 2 3 4 5
#include <bits/stdc++.h>
using namespace std;
const int N=4e5+10;
char s[N];
int ne[N] ;
stack<int> st;
int main(){
while(scanf("%s" , s+1)!=EOF){
//计算字符串s的next数组
ne[0] = ne[1] = 0;
int len = strlen(s+1);
for(int i = 1,j = 0;i < len;i++){
while(j && s[i+1] != s[j+1]) j = ne[j];
if(s[i+1] == s[j+1]) j++;
ne[i+1] = j;
}
//len是必然可行的长度
st.push(len);
while(ne[len] != 0){
st.push(ne[len]);
len = ne[len];
}
//输出
while(!st.empty()){
printf("%d " ,st.top());
st.pop();
}
printf("\n");
}
return 0;
}