蓝桥杯倒计时 41天 - KMP 算法

KMP算法

KMP算法是一种字符串匹配算法,用于匹配模式串P在文本串S中出现的所有位置。

例如S="ababac,P="aba",那么出现的所有位置是13。

在初学KMP时,我们只需要记住和学会使用模板即可,对其原理只需简单理解,不要过度深究,避免把自己绕进去,可以课后自己慢慢去画图理解。

KMP算法将原本O(n2)的字符串匹配算法优化到了O(n).其精髓在于next数组,next数组表示此时模式串下标失配时应该移动到的位置,也表示最长的相同真前后缀的长度。

例如P="ababac",S="abababac"。

当匹配到i=6,j=5,P[i+1]!=S[i]时,j不会移动到1重新开始匹配,而是移动到nex[j]=3继续匹配,

则接下来i=6,j=3,有P[j+1]=S[i],成功匹配,则i,j继续后移,直到i=8.j=6完成一次匹配,则P在S中第一次出现的位置为j-i+1=3。

计算next数组(next数组仅与模式串P有关)的方式就是用P自己去匹配自己,大家只需要掌握模板即可,暂时不要深究其原理。

cpp 复制代码
char s[N],p[N];
int nex[M];
int n = strlen(s+1),m=strlen(p+1);//字符串下标从 1 开始
nex[0]=nex[1]=0;
for(int i=2,j=0;i<=m;++i){
	while(j&&p[i]!=p[j+1])j=nex[j];
	if(p[i]==p[j+1])j++;//从 while 出来后要么 j=0,要么 p[i]==p[j+1],如果匹配成果,则 j 后移
	nex[i]=j;//如果匹配失败就回到 j,因为此时 p[1~j]=p[i-j+1~j]或 j=0(回到最初的地方开始匹配)
}

通过 next 数组匹配

cpp 复制代码
for(int i=1,j=0;i<=n;i++)
{
	while(j&&s[i]!=p[j+1])j=nex[j];
	if(s[i]==p[j+1])j++;
	if(j==m)//成功匹配一次
}

斤斤计较的小Z

思路:KMP 算法模板,不知道为啥结果不对

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N =20,M=20;
char s[N],p[M];
int nex[M];

int main(){
    scanf("%s\n%s",p+1,s+1);
    int n=strlen(s+1),m=strlen(p+1);
    nex[0]=nex[1]=0;
    for(int i=2,j=0;i<=m;i++){
        while(j&&p[j+1]!=p[i])j=nex[j];
        if(p[j+1]==p[i])j++;
        nex[i]=j;
    }
    int res=0;
    for(int i=1,j=0;i<=n;i++){
        while(j&&p[j+1]!=s[i])j=nex[j];
        if(p[j+1]==s[i])j++;
        if(j==m)res++;
    }
    cout<<res<<'\n';
    return 0;
}

boarder

思路:利用 KMP 求整个串的最长真前后缀,len-nex[len]就是整个串的循环节,len 能整除循环节就是答案,不能就是 1。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
char p[N];
int nex[N];
int main( ){
    scanf("%s",p+1);
    unsigned long m=strlen(p+1);
    nex[0]=nex[1]=0;
    for(int i=2,j=0;i<=m;i++){
        while(j&&p[i]!=p[j+1])j=nex[j];
        if(p[i]==p[j+1])j++;
        nex[i]=j;
    }
    int len=m-nex[m];
    if(m%len==0){
        cout<<m/len<<endl;
    }else{
        cout<<1<<endl;
    }
    return 0;
}

幸运字符串


思路:求 nex 数组,找最大值就是答案

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
int n;
char p[N];
int nex[N];
int main( ){
    cin>>n;
    scanf("%s",p+1);
    unsigned long m=strlen(p+1);
    for(int i=2,j=0;i<=m;i++){
        while(j&&p[i]!=p[j+1])j=nex[j];
        if(p[i]==p[j+1])j++;
        nex[i]=j;
    }
    int ans=0;
    for(int i=1;i<=m;i++)ans=max(ans,nex[i]);
    cout<<ans<<endl;
    return 0;
}

你也喜欢幸运字符串吗?

思路:动态规划+KMP,不会。

cpp 复制代码
#include <bits/stdc++.h>
#define ll long long
#define PI 3.1415926
using namespace std;
typedef pair<int, int> vt;
typedef pair<vt, vt> PII;
const int N = 1e6 + 10;
const int M = 2 * N;
const int mod = 998244353;
const ll INF = 0x3f3f3f3f3f3f3f3f;

int ne[N];
string s;
int n;

void solve()
{
    cin >> n;
    cin >> s;
    memset(ne, 0, sizeof ne);
    s = " " + s;

    for (int i = 2, j = 0; i <= n; i++)
    {
        while (j && s[i] != s[j + 1])
            j = ne[j];

        if (s[i] == s[j + 1])
            j++;

        ne[i] = j;
    }

    vector<ll> f(n + 5);
    for (int i = 1; i <= n; i++)
        f[i] = 1;

    for (int i = n; i >= 1; i--)
        f[ne[i]] += f[i];

    ll ans = 0;
    // for(int i=1;i<=n;i++)cout<<f[i]<<endl;
    for (int i = 1; i <= n; i++)
    {
        if (ne[i] != 0)
            ans += f[ne[i]];
    }
    cout << ans << endl;
}

signed main()
{
    ios::sync_with_stdio(false);
    /*多组案例初始化*/
    // int t;cin>>t;while(t--)
    solve();
}
相关推荐
ulias21218 分钟前
leetcode热题 - 5
数据结构·算法·leetcode
Funny_AI_LAB28 分钟前
Naval最新播客谈“氛围编码”:Vibe Coding 开启“一人独角兽”时代
人工智能·算法·语言模型·agi
如何原谅奋力过但无声29 分钟前
【灵神高频面试题合集04-05】二分查找
数据结构·python·算法·leetcode
我不是懒洋洋32 分钟前
【数据结构】排序算法(直接插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序、归并排序、计数排序)
c语言·数据结构·c++·经验分享·算法·排序算法
MediaTea33 分钟前
ML:逻辑回归的基本原理与实现
人工智能·算法·机器学习·数据挖掘·逻辑回归
超级码力6669 小时前
【Latex文件架构】Latex文件架构模板
算法·数学建模·信息可视化
穿条秋裤到处跑9 小时前
每日一道leetcode(2026.04.29):二维网格图中探测环
算法·leetcode·职场和发展
Merlos_wind10 小时前
HashMap详解
算法·哈希算法·散列表
汉克老师10 小时前
GESP2025年3月认证C++五级( 第三部分编程题(1、平均分配))
c++·算法·贪心算法·排序·gesp5级·gesp五级
Yzzz-F13 小时前
Problem - 2205D - Codeforces
算法