【学习笔记】KMP

【学习笔记】KMP

因为字符串对我太抽象了,所以只能以水的心态写这个 KMP。

感觉考到字符串的题肯定要崩。

以下均规定字符串 S 以下标 0 开头。

前缀函数:
给定一个长度为 n 的字符串 S,其前缀函数被定义为一个长度为 n 的数组 π
简单来说 π[i] 就是,子串 s[0i] 最长的相等的真前缀(即不包括字符串 s 本身的前缀)与真后缀的长度。

周期:
S[i]=S[i+p]i=0,,n1p 均成立,则 pS 的一个周期。(1pn)

border:
S[:r)=S[r:),则 rS 的一个 border 的长度。(0r<n)
注意到存在 border r 等价于存在周期 p=Nr

可以发现 π[n] 就 是 最 大 的 border。
进一步地 S 的所有 border 为 π[n],π[π[n]],π[π[π[n]]],
换句话说,利用 π 可以求出 S 的所有周期。

思路(求 π 数组):

  • 对于 1i<n,有 π[i+1]π[i]+1
    实际上 π[i+1]=π[i]+1 当且仅当 S[i]=S[π[i]]

  • 对于 S[i]S[π[i]] 的情形,我们需要找到最大的 j<π[i] 使得 S[:j)=S[ij:i)S[i]=S[j]

  • 可以不断地令 jπ[j] 直至找到满足 S[:j)=S[ij:i)S[i]=S[j]j

附赠字符串下标从 1 开始KMP模板(绝对不是因为从 0 开始写挂了)

π 数组即为 nxt 数组。

注意如何比对两个字符串。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e6+5;

int nxt[N];

int main(){
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	string s1, s2; cin>>s1>>s2;
	int len1 = s1.length(), len2 = s2.length();
    s1 = ' '+s1, s2 = ' '+s2;
	for(int i=2; i<=len2; i++){
		nxt[i] = nxt[i-1];
		while(nxt[i] && s2[nxt[i]+1]!=s2[i]) nxt[i] = nxt[nxt[i]];
		if(s2[nxt[i]+1]==s2[i]) nxt[i]++;
	}
	for(int i=1, j=0; i<=len1; i++){
        // j 是已经匹配完的模式串的最后一位的位置
        while(j && s1[i]!=s2[j+1]) j=nxt[j];
        if(s1[i]==s2[j+1]) j++;
        if(j==len2){
            cout<<i-len2+1<<"\n";
            j = nxt[j]; // 继续匹配
        }
    }
    for(int i=1; i<=len2; i++)
        cout<<nxt[i]<<" ";
	return 0;
}

因为我太菜了,所以 0 下标开始的只能附赠巨佬 @ysl_wf 的代码:
%%%%%%%%%%%%stO巨佬Orz%%%%%%%%%%%%%%

#include<bits/stdc++.h>
using namespace std;

typedef long long lt;
const lt N = 1e6 + 10;
lt pi[N];
lt la, lb, j;
char a[N], b[N];

int main(){
    cin >> a;
    cin >> b;
    la = strlen(a), lb = strlen(b);
    j = -1; pi[0] = -1;
    for(int i = 1; i < lb; i++){
        while(j >= 0 && b[i] != b[j+1]) 
            j = pi[j];
        if(b[j+1] == b[i]) 
            j++;
        pi[i] = j;
    }
    j = -1;
    for(int i = 0; i < la; i++){
        while(j >= 0 && b[j+1] != a[i])
            j = pi[j];
        if(b[j+1] == a[i])
            j++;
        if(j == lb-1){
            printf("%lld\n", i-lb+2);
            j = pi[j];
        }
    }
    for(int i = 0; i < lb; i++){
        printf("%lld ", pi[i]+1);
    }
}
posted @   FlyPancake  阅读(13)  评论(3编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
// music
点击右上角即可分享
微信分享提示