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; }
分类:
算法
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)