忘了具体什么时候写的,应该是 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]);
}
标签:字符,匹配,int,代码,算法,nex,KMP
From: https://www.cnblogs.com/NBest/p/17745589.html