KMP—字符串匹配
介绍:
如果有一个题目,说明的是在一个母串之中找到一个字符串s出现了几次的时候,问你输出次数?
1.暴力,枚举每个位置 时间复杂度 O(N*M)
2.使用KMP 时间复杂度 O(M+N)
1.暴力的算法
无需介绍吧!
直接上算法!
#include <bits/stdc++.h>
using namespace std;
int main()
{
string mother,s;
int ans=0;
cin>>mother>>s;
int len=s.length();
int l=mother.length();
for(int i=0; i<l-len+1; i++)
{
string t;
t=mother.substr(i,len);
if(t==s)
ans++;
}
printf("%d\n",ans);
return 0;
}
2.KMP算法
先上算法,之后再来讲解,呵呵!
#include <iostream>
#include <stdio.h>
#include <map>
#include <cstring>
using namespace std;
const int N=100005;
int p[N];
char s[N],t[N*10];
void getp(int n)
{
p[0]=-1;
for(int i=1,j=-1; i<=n; i++)
{
while(j>=0&&s[j+1]!=s[i])
{
j=p[j];
}
p[i]=++j;
}
}
int KMP(int n,int m)
{
int ret=0;
for(int i=1,j=0; i<=m; i++)
{
while(j>=0&&s[j+1]!=t[i])
{
j=p[j];
}
j++;//ºóɨһλ!!
if(j==n)
{
ret++;
j=p[j];
}
}
return ret;
}
int main()
{
int n;
scanf("%d",&n);
while(n--)
{
scanf("%s",s+1);
int n=strlen(s+1);
getp(n);
scanf("%s",t+1);
int m=strlen(t+1);
printf("%d\n",KMP(n,m));
}
}
分析
对于一个字符串的匹配,如果找的了NO.i的位置无法匹配,我们通常会开始继续匹配,但是比如abababa,后来又来了ababca,很明显,在s的char c位置出现了不够匹配,但是,如果是暴力的话就是之后继续判断,但是可以相处一个使得最大的前缀和等于后缀和的是将,储存在花掉匹配的i点记为p[i]就好了!,可能不会动,但是就附上图片吧!
那么这么预处理呢?
就是这个函数:
void getp(int n)
{
p[0]=-1;
for(int i=1,j=-1; i<=n; i++)
{
while(j>=0&&s[j+1]!=s[i])
{
j=p[j];
}
p[i]=++j;
}
}
p[0]此时的作用只是为了辅助p[1~len]的促成罢了,但是j=-1的初始化真的要好好的去理解啊!while循环的跳出条件就是
while(j>=0&&s[j+1]!=s[i])
那么为什么是这两个数据呢
1.j>=0,就是说万一根本没有最长前缀的事情,所以就跳出来,恰是-1,然后j++,就片尾了0,再赋值,之后就会使得i+1的位置是带着p[i]=0,的情况下去去搜索,去预处理,是不是很妙?
2.这是因为这个图!
所以是while (s[i]!=s[j+1])
十分好玩!比较的函数KMP!
代码:
int KMP(int n,int m)
{
int ret=0;
for(int i=1,j=0; i<=m; i++)
{
while(j>=0&&s[j+1]!=t[i])
{
j=p[j];
}
j++;
if(j==n)
{
ret++;
j=p[j]; //*********
}
}
return ret;
}
只要还是注释带”*”号的地方,就是将来说如果匹配成功的话就继续匹配,在p[j]的位置上下去就好了!,但是有一些题目j=0,为什么?比如
剪花布条
因为这个题目是减下来的,也就是说剪掉就没了!
注释:
注:
之后还有更多好玩的KMP玄学问题,暂等更新……