【算法学习】KMP 算法

学习笔记

我认为我这个算法可能无法讲明白,而且工作量巨大,所以为了让你快速学会我推荐学习下列笔记。

学习笔记1

学习笔记2

学习笔记3

放个模板

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
char a[N],b[N];
int lena,lenb;
int nxt[N];
 
int main(){
    ios::sync_with_stdio(false);
    cin>>a+1;
    cin>>b+1;
    lena=strlen(a+1);
    lenb=strlen(b+1);
    
    int j=0;
    
    for(int i=2;i<=lenb;i++){
    	while(j&&b[i]!=b[j+1]){
    		j=nxt[j];
		}
		if(b[j+1]==b[i]){
			j++;
		}
		nxt[i]=j;
	}
    j=0;
    for(int i=1;i<=lena;i++){
    	while(j&&a[i]!=b[j+1]){
    		j=nxt[j];
		}
		if(b[j+1]==a[i]){
			j++;
		}
		if(j==lenb){
			cout<<i-lenb+1<<"\n";
		}
	}
    
   	for(int i=1;i<=lenb;i++){
   		cout<<nxt[i]<<" ";
	} 
    
    return 0;
}

例题

感觉经过上述的学习,你一定有所收获吧(如果没有的话还是菜就多练吧),所以接下来我会举出一些题目,应该会对你的学习有些帮助。

1.【模板】KMP(洛谷P3375)

如果你学 KMP 请先会做对这道题,这将会检验你的代码能力,请务必做对。

2.字符串中的最大值(51nod P1277)

这道题是对 KMP nxt数组 很好的应用,题意是求其所有前缀中,字符长度与出现次数的乘积的最大值。

我们可知 nxt 数组指示了当前模式串在该位置失配时,应该将模式串的哪一位与此位对齐,所以我们可以从后向前统计每个前缀的出现次数 \(res[]\),并将 \(res[nxt[i]]+=res[i]\) ,最后再从前向后计算最大值。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e6+10;
ll res[N];
char b[N];
int nxt[N];
int lenb;

int main(){
    ios::sync_with_stdio(false);
	
	cin>>b+1;
	
	lenb=strlen(b+1);
	
	int j=0;
	
	for(int i=2;i<=lenb;i++){
		while(j&&b[i]!=b[j+1]){
			j=nxt[j];
		}
		if(b[i]==b[j+1]){
			j++;
		}	
		nxt[i]=j;
	}
	ll ans=0;
	
	for(int i=lenb;i>=1;i--){
		res[i]++;
		if(nxt[i]!=0){
			res[nxt[i]]+=res[i];
		}
	}
	
	for(int i=1;i<=lenb;i++){
//		cout<<res[i]<<" ";
		ans=max(ans,res[i]*i);
	} 
	cout<<ans;
    return 0;
}

2.逆向工程(51nod P3118)

nxt 第 \(i\) 位不等于 0 时可以求出,若为 0 时,需不断向前找 nxt 将遇到的数字打标记,然后从小到大找第一个没打过标记的字母即可。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+10;
int n;
int nxt[N];
char b[N];
int a[30];

int main(){
    ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>nxt[i];
	}
	b[1]='a';
	for(int i=2;i<=n;i++){
		if(nxt[i]!=0){
			b[i]=b[nxt[i]];
		}
		else{
			int j=nxt[i-1]+1;
			while(1){
				a[b[j]-'a']=1;
				if(j==1){
					break;
				}
				j=nxt[j-1]+1;
			}
			int flag=0;
			for(int k=0;k<26;k++){
				if(a[k]==1){
					a[k]=0;
					continue;
				}
				if(a[k]==0&&flag==0){
					b[i]=k+'a';
					flag=1;
				}
			}
		}
	}
	for(int i=1;i<=n;i++){
		cout<<b[i];
	}
    return 0;
}
posted @ 2024-09-01 19:57  sad_lin  阅读(10)  评论(0编辑  收藏  举报