KMP 字符匹配

忘了具体什么时候写的,应该是 2023.8 初

这算是个算法复习,因为我太菜了以前学的都不会了。

KMP 字符匹配

有一说一这个我讲不来,大概意思就列这好了:

Knuth(D.E.Knuth)&Morris(J.H.Morris)&Pratt(V.R.Pratt) 提出的字符串匹配算法,简称 KMP。

KMP 算法应该是最基础的字符串匹配算法了,基本原理是最大化利用已经处理过的数据,减少重复运算,然后达到线性复杂度。

KMP 算法解决的最一般的问题就是给定一个长串和一个短串,问你短串在长串中出现的位置和次数。如果我们用朴素的暴力算法自然就是一个一个比对过去,如果有错误就重新开始,如果正确就输出,大概代码为:

cin>>s>>l;
for(int i=0;i<s.length();i++){
    for(int j=0;j<l.length();j++){
        if(s[i+j]!=l[j])break;
        if(j==l.length()-1)printf("%d\n",i);
    }
}

然后这个代码居然一不小心给 KMP 模板过了?(border 是用 KMP 求的)

咳咳,反正这个代码在最优情况下虽然也是线性,但是显然很容易卡到 \(O(|s||l|)\) 的复杂度,原因是什么呢?

比如:

abcab
abcacababcab

正常我们是要一步一步比对的,发现错误之后移动一格继续匹配,而我们发现前四个都相同,我们这时不需要比较前四个,而是直接移动到查找不一样的。

   abcab
abcacababcab

反正大概就是对于前后缀相同已经判断过的可以不用继续判断,而是通过前一位的答案跳到一个位置。

那怎么知道应该跳到哪个位置?其实只需要先跟自己比较搞出一个 nex 数组即可。

nex 数组存的是这一位之前的最大前缀等于后缀的长度(非本身)。转移只需要找比较上个位置的 nex 的下一个是否相同即可,具体看代码吧。

\(Code\)

int nex[1000002],n,m;
char s[1000004],l[1000002];
void init(){
   int los=0;
   for(int i=2;i<=m;i++){
       while(los&&l[i]!=l[los+1])los=nex[los];//不匹配跳到上一个位置
       if(l[los+1]==l[i])los++;//匹配就加
       nex[i]=los;//更新
   }
}
int main(){
   scanf("%s%s",s+1,l+1);
   n=strlen(s+1),m=strlen(l+1);
   init();
   int j=0;
   for(int i=1;i<=n;i++){
       while(j&&l[j+1]!=s[i])j=nex[j];//不匹配跳上一个
       if(l[j+1]==s[i])j++;
       if(j==m)printf("%d\n",i-m+1),j=nex[j];//找完了跳回去看看能不能继续匹配
   }
   for(int i=1;i<=m;i++)printf("%d ",nex[i]);
}
posted @ 2023-10-07 09:46  NBest  阅读(21)  评论(0)    收藏  举报