mmxingye

导航

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

image-20220617190948632

#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;
}

posted on 2022-06-17 19:39  独立树  阅读(228)  评论(0编辑  收藏  举报