【学习笔记】KMP

【学习笔记】KMP

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

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

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

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

周期:
\(S[i]=S[i+p]\)\(i=0,\ldots,n-1-p\) 均成立,则 \(p\)\(S\) 的一个周期。\((1\leq p\leq n)\)

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

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

思路(求 \(\pi\) 数组):

  • 对于 \(1\leq i<n\),有 \(\pi[i+1]\leq\pi[i]+1\)
    实际上 \(\pi[i+1]=\pi[i]+1\) 当且仅当 \(S[i]=S[\pi[i]]\)

  • 对于 \(S[i]\neq S[\pi[i]]\) 的情形,我们需要找到最大的 \(j<\pi[i]\) 使得 \(S[:j)=S[i-j:i)\)\(S[i]=S[j]\)

  • 可以不断地令 \(j\leftarrow\pi[j]\) 直至找到满足 \(S[:j)=S[i-j:i)\)\(S[i]=S[j]\)\(j\)

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

\(\pi\) 数组即为 \(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 @ 2024-07-31 19:22  FlyPancake  阅读(27)  评论(3)    收藏  举报
// music