KMP算法

border

border是指字符串最长公共前后缀。

例如:ababa这个字符串,它的前缀是a,ab,aba,abab它的后缀是a,ba,aba,baba。在前缀与后缀中相同的最长为aba,因此aba为它的border。

这里前缀和后缀都不能与原字符串等长。因此,border也不能与原字符串等长。

Next表

定义

Next表是一个存储border的表。Next[i]表示以i结尾的非前缀子串与到i为止的前缀最长匹配长度。即以i结尾的前缀的border。

例如:ababa。Next[0]必然是0。因为以0结尾的前缀为a,它的border不能与原字符串等长,即不能大于一,因此它没有border。接着是Next[1],前缀为ab,这个子串前后缀没有公共部分,因此border也为0,所以Next[1]=0。然后Next[2],前缀aba,border为1,因此Next[2]=1。以此类推,得到此字符串Next表为0 0 1 2 3。

求字符串Next表

若t[Next[i]+1]=t[i+1],则Next[i+1]=Next[i]+1。

此时已知以i结尾的前缀有border,若字符向后一位与border向后一位相等,那么字符向后一位的border必然也加一。

例如当字符串为ababa,i=2时,此时t[i]为第二个'a',t[i+1]为b。这个例子中Next[i+1]=Next[i]+1,即2。

若t[Next[Next[i]]+1]=t[i+1],则Next[i+1]=Next[Next[i]]+1。即若前缀往后一位与前缀border往后一位不相同,则往前找border的border。直到出现相同的为止。因此,则需要不断将Next[Next[i]]套上新的Next[i],直到满足第一个式子。

以下为代码:

void get_next(string t){
	int i,x;
	for(nxt[1]=x=0,i=2;t[i]!='\0';i++){
		while(x&&t[i]!=t[x+1])x=nxt[x];
		if(t[i]==t[x+1])x++;nxt[i]=x;
	}
}

KMP

问题

【模板】KMP - 洛谷

给定两个字符串s1,s2,若s1中存在一子串[l,r]与s2完全相同,则称s2在s1中出现一次,位置为l。

求出s2出现的次数及位置。

KMP算法通常用于求解此类一个串在另一个串中出现的问题。

其中s2叫做模式串,s1叫做主串。

算法

首先从主串第一位开始向后遍历。若出现模式串与主串不一样时,寻找已遍历的前缀border,若border,并将模式串已遍历border前缀与主串已遍历border后缀对齐,继续向后遍历。

代码如下:

void kmp(string s,string t){
	int i=1,j=0,k=0;
	for(;i<=s.size();i++){
		while(j&&s[i]!=t[j+1])j=nxt[j];
		if(s[i]==t[j+1])j++;
		if(!t[j+1])j=nxt[j];
	}
}

有了求next表的代码和kmp的代码之后,就可以完成完整的kmp算法了。

KMP模板题AC代码

#include<bits/stdc++.h>
#define TIE ios::sync_with_stdio(false),cin.tie(0),cin.tie(0);
using namespace std;
const int N=1e6+5;
string s1,s2;int nxt[N];
void get_next(string t){
	int i,x;
	for(nxt[1]=x=0,i=2;t[i]!='\0';i++){
		while(x&&t[i]!=t[x+1])x=nxt[x];
		if(t[i]==t[x+1])x++;nxt[i]=x;
	}
}
void kmp(string s,string t){
	int i=1,j=0,k=0;
	for(;i<=s.size();i++){
		while(j&&s[i]!=t[j+1])j=nxt[j];
		if(s[i]==t[j+1])j++;
		if(!t[j+1]){cout<<i-j+1<<'\n';j=nxt[j];}
	}
}
signed main(){
	TIE;cin>>s1>>s2;s1=" "+s1;s2=" "+s2;get_next(s2);kmp(s1,s2);
	for(int i=1;i<s2.size();i++)cout<<nxt[i]<<' ';
	return 0;
}

题目:无线传输

题目描述

 字符串s2循环拼接成一个字符串,给出这个字符串的一个子串s1。求s2最短长度。

思路

这个题就是给出一个字符串子串,求其最小循环节长度。

有一个定理:s最小循环节长度=|s|-border

因此只需要求出s2的border即可。

AC代码

  • 32
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值