KMP算法

KMP算法

字符串算法


给定两个字符串s1和s2,求s2在s1中所有的出现位置

暴力(O(n*m)):

#include<bits/stdc++.h>
using namespace std;
string s1,s2;
int main(){
scanf("%s%s",s1,s2);
int n1=strlen(s1),n2=strlen(s2);
for(int i=1;i<=n1-n2+1;i++){
bool pd = 0;
for(int j=1;j<=n2;j++) if(s1[i+j-1]!=s2[j]){
pd = 1;
break;
}
if(!pd) print("%d",i+n2-1);
}
return 0;
}

正题 KMP

整体感官

假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置

  • 如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++,继续匹配下一个字符;

  • 如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]。此举意味着失配时,模式串P相对于文本串S向右移动了j - next [j] 位。

    • 换言之,当匹配失败时,模式串向右移动的位数为:失配字符所在位置 - 失配字符对应的next 值,即移动的实际位数为:j - next[j],且此值大于等于1。

原理

利用最大的相同前缀后缀(以下记作maxsame)缩短搜索时间

eg. A B C D A B C

      same={A B C}

失配时可利用前后缀相同的性质将短串移动到相同前缀的后一位

next[]

next[]指当失配时将当前长串的匹配位与短串的何位置相对齐

即短串当前匹配位之前所有字符组成的字符串的maxsame长度

第一个取-1

递归求next[]

void getNext(){
int len=strlen(p);
next[0]=-1;
int k=-1;//记忆化 存储当前已有的maxsame长度
int j=0;//当前下标
while(j<len-1){
if(k==-1||p[j]==p[k]){
++k;
++j;
next[j]=k;//把当前匹配位之前所有字符组成的字符串的maxsame长度赋给next
}else{
k=next[k];
}
}
return;
}

若p[j]!=p[k],

将k换成当前maxsame的maxsame再进行匹配

组合到一块

模板:P3375

AC代码:

#include<bits/stdc++.h>
using namespace std;
int next[10000001];
char s1[10000001],s2[10000001];
void getNext(){
int len=strlen(s2);
next[0]=-1;
int k=-1;
int j=0;
while(j<len){
if(k==-1||s2[j]==s2[k]){
++k;
++j;
next[j]=k;
}else{
k=next[k];
}
}
return;
}
void KMP(){
int len1=strlen(s1);
int len2=strlen(s2);
int i=0,j=0;
while(i<len1&&j<len2){
if(j==-1||s1[i]==s2[j]){
j++;
i++;
}else{
j=next[j];
}
if(j==len2){
printf("%d\n",i-j+1);
j=next[j];
}
}
for(int i=1;i<=strlen(s2);i++){
printf("%d ",next[i]);
}
return;
}
int main(){
scanf("%s%s",s1,s2);
getNext();
KMP();
return 0;
}

P2375

P3435

P3426



posted @ 2021-01-02 18:53  wsy_jim  阅读(250)  评论(0编辑  收藏  举报