KMP

KMP

背景

KMP作为sharpland压了很多年的算法,NOIP已经很久没有考过了。。。

概念

KMP算法是一种改进的字符串匹配算法,KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。时间复杂度O(m+n)。【来自百度百科】

原理

KMP分两步:

  1. str1自身匹配,创造next数组

    next【i】存储的是j
    j满足str1【1-j】== str1【(i-j+1)~i】,且最大;(i != j)
    为什么要这样做?
    因为在str1和str2等其他字符串每次匹配失败后,要将str1后移,将next【i】移到i位,那么next【i】及之前也都是匹配的,然后继续匹配。
    如图

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

实现

void make_()
{
	int j=0;
	next[0]=0;
	for(int i=2;i<=len2;++i)
	  {
	  	while (j>0&&s2[j+1]!=s2[i]) j=next[j];
	  	if (s2[j+1]==s2[i]) ++j;
	  	next[i]=j;
	  }
}
  1. 然后和str2及其他字符串匹配。
void KMP()
{
	int j=0;
	for(int i=1;i<=len1;++i)
	  {
	  	while (j>0&&s2[j+1]!=s1[i]) j=next[j];
	  	if (s2[j+1]==s1[i]) j++;
	  	if (j==len2)
	  	  ans++,j=next[j];
	  }
}

用处

  1. KMP可以较高效率解决字符串匹配问题。
  2. KMP还可以处理每个前缀在整个字符串中出现的次数。
for(int i=len;i>=1;--i)  
	  {
	  	sum[i]++;
	  	sum[next[i]]+=sum[i];
	  }

题面1

[Usaco2015 Feb]Censoring
有一个S串和一个T串,长度均小于1,000,000,设当前串为U串,然后从前往后枚举S串一个字符一个字符往U串里添加,若U串后缀为T,则去掉这个后缀继续流程。
样例数据
input

whatthemomooofun
moo

output

whatthefun

解析

这个是KMP加上栈了,将U串看作栈,然后模拟。
其中关键是栈每一层应该有一个标记,这样要连续清栈时可以马上找到str1应该摆放的位置。
非常灵活。

代码

#include<bits/stdc++.h>
using namespace std;
char a[1000010];
char b[1000010];
char st[1000010];
int lena,lenb,top=0,next[1000010]={},f[1000010]={};
void init()
{
	scanf("%s",a+1);
	scanf("%s",b+1);
	lena=strlen(a+1);
	lenb=strlen(b+1);
}
void make_()//next数组操作 
{
	int j=0;
	for(int i=2;i<=lenb;++i)
	  {
	  	while (j>0&&b[j+1]!=b[i]) j=next[j];
	  	if (b[j+1]==b[i]) j++;
	  	next[i]=j;
	  }
}
void KMP() 
{
	int j=0;
	for(int i=1;i<=lena;++i)
	  {
	  	st[++top]=a[i];//每个都进栈 
	  	while (j>0&&b[j+1]!=a[i]) j=next[j];
	  	if (b[j+1]==a[i]) j++;
	  	f[top]=j;
	  	if (j==lenb)
	  	  {
			top-=lenb;//清栈 
			j=f[top];//核心之处!!!清栈后指向top所匹配的str1的位置。 
		  }
	  }
}
void outit()
{
	for(int i=1;i<=top;++i)
	  printf("%c",st[i]);
}
int main()
{
	init();
	make_();
	KMP();
	outit();
	return 0;
}

题面2

[Baltic2009]Radio Transmission
题目描述
给你一个字符串,它是由某个字符串不断自我连接形成的。

但是这个字符串是不确定的,现在只想知道它的最短长度是多少.

输入格式
第一行给出字符串的长度,1 < L ≤ 1,000,000. 第二行给出一个字符串,全由小写字母组成.

输出格式
输出最短的长度

样例数据
input

8
cabcabca

output

3
对于样例,我们可以利用"bca"不断自我连接得到"abcabcabc",读入的cabcabca,是它的子串

解析

KMP的next处理后
n-next【n】就是答案。

代码

#include<bits/stdc++.h>
using namespace std;
int n;
char s[2000010];
int next[2000010]={};
void init()
{
	scanf("%d",&n);
	scanf("%s",s+1);
}
void make_()
{
	int j=0;
	for(int i=2;i<=n;++i)
	  {
	  	while (j>0&&s[j+1]!=s[i]) j=next[j];
	  	if (s[j+1]==s[i]) j++;
	  	next[i]=j;
	  }
}
int main()
{
	init();
	make_();
	printf("%d",n-next[n]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值