KMP
背景
KMP作为sharpland压了很多年的算法,NOIP已经很久没有考过了。。。
概念
KMP算法是一种改进的字符串匹配算法,KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。时间复杂度O(m+n)。【来自百度百科】
原理
KMP分两步:
-
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;
}
}
- 然后和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];
}
}
用处
- KMP可以较高效率解决字符串匹配问题。
- 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;
}