KMP Я пришёл!

kmp算法是用来对字符串进行匹配的一种算法。对于长度为n的字符串N,要查找其中长度为m的一个字符串M,传统的暴力方法的复杂度为O( nm ),而用kmp算法时间复杂度为O( m+n )。*

所谓KMP最重要的就是next[]数组 他表示的意思是 在当前下标i下 最长的前缀和后缀相等的长度
这里的下表要与字符串下标区分开
例如 abcabc 初始next[0]=-1;
next[1]=0; next[2]=0; next[3]=0; 到第四个字母位置时末尾a 和 开端a相等所以next[4]=1; 又因为b和b相等 所以next[5]=2;
然后紧接着 c和c相等 next[6]=3;

例如 abcababc
next[0]=-1; next[1]=0; next[2]=0; next[3]=0; next[4]=1; next[5]=2; next[6]=1; next[7]=2; next[8]=3;
那如何计算的呢

如果一直相等 就一直递增 否则就重置一下
计算得到next数组
然后在主串中找子串的时候 就可以利用next数组来大大的优化,根据相同的前缀后缀可以省略很多比较

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
const int maxn=1e6+88;

ll nex[maxn];
void slu(string s){
	int i=0,k=-1;
	nex[0]=-1;
	while(i<s.size())
	{
		if(k==-1|| s[i]==s[k] ){
			i++;
			k++;
			nex[i]=k;
		}
		else{
			k=nex[k];
		}
	}
}
void kmp(string s1,string s2){
	int l1=s1.size(),l2=s2.size();
	
	int i=0,j=0;
	while(i<l1&&j<l2){
		if(j==-1||s1[i]==s2[j]){
			
			j++;
			i++;
			if(j==l2)
			{
				cout<<"YES";
				return;
			}
		}else{
			j=nex[j];
		}
	}
	cout<<"NO";
}
int main(){
	string s1,s2;
	cin>>s1>>s2;
	slu(s2);
	
	for(int i=0;i<=s1.size();i++){
		cout<<nex[i]<<endl;
	}
	
	kmp(s1,s2);
	return 0;
}

在这里插入图片描述
在这里插入图片描述

输入描述:在这里插入图片描述
输出描述:
Your program is to write to standard output. Print two integers k and p in one line.
The following shows sample input and output for two test cases.
链接:https://ac.nowcoder.com/acm/contest/13506/I
来源:牛客网

输入
复制
6
612534 3157 423 3157 423 3157
输出
复制
1 2
示例2
输入
复制
9
1 2 1 3 1 2 1 3 1
输出
复制
0 4

本质求周期,将数字当成字符来看 即可 套kmp模板

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
const int maxn=1e6+88;
//char s1[maxn];
ll s2[maxn];
ll nex[maxn];
void slu(){
	int i=0,k=-1;
	nex[0]=-1;
	while(i<n)
	{
		if(k==-1|| s2[i]==s2[k] ){
			i++;
			k++;
			nex[i]=k;
		}
		else{
			k=nex[k];
		}
	}
}
/*void kmp(){
	int l1=strlen(s1),l2=strlen(s2);
	
	int i=0,j=0;
	while(i<l1&&j<l2){
		if(j==-1||s1[i]==s2[j]){
			
			j++;
			i++;
			if(j==l2)
			{
				cout<<i-l2+1<<endl;
				return;
			}
		}else{
			j=nex[j];
		}
	}
	cout<<0<<endl;
}*/
int main(){
	
	scanf("%d",&n);
	for(int i=n-1;i>=0;i--)
	{
		scanf("%d",&s2[i]);
	}
	slu();
	ll sum=1999999999;
	ll k,p;
	for(int i=n;i>=1;i--)
	{
		int tk=n-i;
		int tp=i-nex[i];
		if(tk+tp<sum||(tk+tp==sum&&tp<p)){
			sum=tk+tp;
			p=tp;
			k=tk;
		}
	}
	
	cout<<k<<' '<<p;
	
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

牛郎恋刘娘,刘娘念牛郎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值