KMP这个算法几个月前就听了好多遍也没有搞懂,就跳过了,现在老师要求让写,就又搞了好久,就现在而言,好像懂了,又好像有点迷糊。在老师讲过话又补充了一点,如果哪位大佬不小心看到了,发现第二块段代码有啥问题,一定要跟我讲哦,毕竟我很菜,自己搞得,也不知道对不对。
字符串匹配
BF暴力做法:
#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int main()
{
int n,m;
char s[20],p[20];
cin>>n>>s+1>>m>>p+1;
//这个代码是最终如果没有匹配的就输出0,有匹配成功了就输出1
//BF算法,暴力时间复杂度O(m*n)
int t=1;
for(int i=1;i<=n;i++)
{
t=1;
for(int j=1;j<=m;j++)
{
if(s[i+j-1]!=p[j])
{
t=0;
break;
}
}
if(t==1) break;
}
if(t==1) cout<<1<<endl;
else cout<<0<<endl;
return 0;
}
KMP:
重点放到模式串P上
10.9今天我们老师又讲了kmp的ne数组的计算方法,按照他讲的来看一遍,这个ne应该是每次不匹配时直接把j这个指针挪到模式串P的哪个位置,但是这个求的ne好像只可以找到第一次出现的位置:
1.首先初使ne[1]=0,ne[2]=1;
2.接下来后面求解每一位的ne时,根据前一位进行比较;首先将前一位和它的ne对应的字符进行比较:
(1).如果相等,那么这一位(第i位)的ne就等于前一位(第i-1位)的ne+1:
(比如当前求的是第三位P[3]的ne值,即想要求ne[3],那么你就看P[2],就是比较P[2]是否等于 于P[ne[2]],P[2]它的ne[2]=1,即看P[2]是否等于P[1],等于了那么ne[3]=ne[2]+1=2);
(2).如果不等,那么向前继续寻找ne对应的字符与前一位(第i-1位)的字符比较,直到找到了某一位(记作第m位,显然m一定小于i-1)的ne对应的字符与前一位(第i-1位)的字符相等,那么这一位的ne就等于ne[m]+1;如果第m位的ne等于1,然后P[1]还不等于前一位(第i-1位)的字符,那么ne[i]=1:
(比如当前求的是第三位P[3]的ne值,即想要求ne[3],你发现P[2]!=P[ne[2]],就是P[2]!=P[1],那么ne[3]=1;)
下面一个例子,模式串P为 :abcaabbabcab(下标都是从1开始):
首先ne[1]=0,ne[2]=1;
ne[3]:P[2]='b',ne[2]=1,P[2]!=P[1],所以ne[3]=1;
ne[4]:P[3]='c',ne[3]=1,P[3]!=P[1],所以ne[4]=1;
ne[5]:P[4]='a',ne[4]=1,P[4]==P[1],所以ne[5]=ne[4]+1=2;
ne[6]:P[5]='a',ne[5]=2,P[5]!=P[ne[5]],所以你看P[ne[ne[5]]=P[1],发现这个时候P[1]==P[5]了,所以ne[6]=ne[ne[5]]+1,所以ne[6]=2;
ne[7]:P[6]='b',ne[6]=2,P[6]==P[ne[6]],所以ne[7]=ne[6]+2=3;
同理可继续往下算得到该例子P的ne数组的值为ne={0,1,1,1,2,2,3,1,2,3,4,5};
我太饿了,代码下午下课之后再写
我回来了!
上述逻辑代码如下:
#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int main()
{
int n,m;
char s[20],p[20];
cin>>n>>s+1>>m>>p+1;
//求ne
int ne[20];
memset(ne,0,sizeof(ne));
ne[1]=0;
ne[2]=1;
for(int i=3;i<=m;i++)
{
if(p[i-1]==p[ne[i-1]]) ne[i]=ne[i-1]+1;
else
{
int j=ne[i-1];
while(j!=0 && p[i-1]!=p[j]) j=ne[j];
j+=1;
ne[i]=j;
}
}
//for(int i=1;i<=m;i++) cout<<ne[i]<<endl;
for(int i=1,j=1;i<=n;i++)
{
while(j!=0 && s[i]!=p[j]) j=ne[j];
if(j==0) j=1;
if(s[i]==p[j]) j++;
if(j-1==m)
{
cout<<i-m+1<<endl;//输出模式串在主串的位置
j=ne[j-1]+1;
}
}
return 0;
}
求nextval数组值
求nextval数组值有两种方法,一种是不依赖next数组值直接用观察法求得,一种方法是根据next数组值进行推理,两种方法均可使用,视更喜欢哪种方法而定。
例如主串为“aaabaaaab”、
模式串为“aaaab”
1.第一位的nextval值必定为0,第二位如果与第一位相同则为0,如果不同则为1。
2.第三位的next值为1,那么将第三位和第一位进行比较,均为a,相同,则,第三位的nextval值为0。
3.第四位的next值为2,那么将第四位和第二位进行比较,不同,则第四位的nextval值为其next值,为2。
4.第五位的next值为2,那么将第五位和第二位进行比较,相同,第二位的next值为1,则继续将第二位与第一位进行比较,不同,则第五位的nextval值为第二位的next值,为1。
5.第六位的next值为3,那么将第六位和第三位进行比较,不同,则第六位的nextval值为其next值,为3。
6.第七位的next值为1,那么将第七位和第一位进行比较,相同,则第七位的nextval值为0。
7.第八位的next值为2,那么将第八位和第二位进行比较,不同,则第八位的nextval值为其next值,为2。
y总讲的是:
ne数组的概念就是:比如ne[i]=j就是以i为终点的后缀和以1为起点的前缀的字符串相等的最大长度,就是每次发生不匹配时,模式串移动的距离
比如字符串p:abcdabc,则ne[1]=0,ne[2]=0,ne[3]=0,ne[4]=0,ne[5]=0,ne[6]=2,ne[7]=3; 显然以s[1]为后缀的长度为0;
以s[2]=b为终点的后缀有{b},以1为起点的前缀有{a},相等的最大长度为0;
对于ne[6]=2来说,以s[6]=b为终点的后缀有{b,ab,dab,cdab,bcdab},以1为起点的前缀有{a,ab,abc,abcd,abcda},发现前缀等于后缀的最大长度是2
//问题就在如何计算ne数组,事实上它就是模式串自己与自己匹配的一个过程。
(为什么每次j都从0开始呢,因为对于主串s和模式串p的比较,每次p中试图与s[i]比较的是p[j+1],所以对j总是向前错一位)
总代码如下:(yxc)
#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int main()
{
int n,m;
char s[20],p[20];
cin>>n>>s+1>>m>>p+1;
//KMP算法,下面是字符串下标以1开始
int ne[200];
memset(ne,0,sizeof(ne));
for(int i=2,j=0;i<=m;i++)
{
while(j && p[i]!=p[j+1]) j=ne[j];
if(p[i]==p[j+1]) j++;
ne[i]=j;
}
for(int i=1,j=0;i<=n;i++)
{
while(j && s[i]!=p[j+1]) j=ne[j];
if(s[i]==p[j+1]) j++;
if(j==m)
{
cout<<i-m+1<<' ';
j=ne[j];
}
}
return 0;
}