一些可以帮助你理解KMP的题(题解报告)

本文分享了字符串处理中的关键技巧,包括KMP算法在[BOI2009]RadioTransmission无线传输、[POI2006]OKR-PeriodsofWords中的应用,以及字符串哈希的原理和一道经典匹配问题[CensoringSP2375]的解法。通过实例解析next数组在这些问题中的核心作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


今天大佬扔给我一个题单 题目难度递增非常友好( 骗谁呢
拿走不谢
然后最近我在做字符串的题就顺着字符串部分做了一些
有字符串哈希和KMP的
字符串哈希只做了一道模板题 其他两道太难了
KMP做的比较多这里就总结一下咯
哈希就改天吧emmm

P4391 [BOI2009]Radio Transmission 无线传输

题目链接
这道题最简单 循环节长度直接 n - next[n]搞定
AC代码:

#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;
const int N = 1e6 + 10;
int n;
char s[N];
int ne[N];
void getnext(){
    int j = 0,k = -1;
    ne[j] = k;
    while(j < n){
        if(k == -1 || s[j] == s[k]) ne[++ j] = ++ k;
        else k = ne[k];
    }
}
int main(){
    scanf("%d",&n);
    scanf("%s",s);
    getnext();
    printf("%d\n",n - ne[n]);
    return 0;
}

P3435 [POI2006]OKR-Periods of Words

题目链接
这道题可以帮助你理解next数组的本质
题目就是要你求每一个前缀的最小的不为零的next
看个图帮助理解
在这里插入图片描述
如图我们要让紫色的部分最小化,也就是求最小的next啦
那么如何求最小的next呢
直接next[next[i]]下去就行了,直到next为0就停止,自己想想是为什么呢
AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;
const int N = 1e6 + 10;
int n;
char s[N];
int ne[N];
long long ans;
void getnext(){
    int j = 0,k = -1;
    ne[j] = k;
    while(j < n){
        if(k == -1 || s[j] == s[k]) ne[++ j] = ++ k;
        else k = ne[k];
    }
}
int main(){
    scanf("%d",&n);
    scanf("%s",s);
    getnext();
    for(int i = 2;i <= n;i ++){
        int t = i;
        while(ne[t]) t = ne[t];
        if(ne[i]) ne[i] = t;
        ans += i - t;
    }
    printf("%lld\n",ans);
    return 0 ;
}

P4824 [USACO15FEB]Censoring S

题目链接
这是一道匹配并删除子串的问题,也比较经典
之前碰到过但是没了解清楚,现在终于搞清楚了
KMP匹配,再利用一个栈就行了
具体实现看代码:
AC代码:

#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;
const int N = 1e6 + 10;
int sl,tl;
int ne[N],f[N];
char s[N],t[N];
int stk[N],top;
void getnext(){
    for(int i = 2, j = 0;i <= tl;i ++){
        while(j && t[i] != t[j + 1]) j = ne[j];
        if(t[i] == t[j + 1]) j ++;
        ne[i] = j;
    }
}
void KMP(){
    for(int i = 1,j = 0;i <= sl;i ++){
        while(j && s[i] != t[j + 1]) j = ne[j];
        if(s[i] == t[j + 1]) j ++;
        f[i] = j;
        stk[++ top] = i;
        if(j == tl) top -= tl,j = f[stk[top]];
    }
}
int main(){
    scanf("%s%s",s + 1,t + 1);
    sl = strlen(s + 1),tl = strlen(t + 1);
    getnext();
    KMP();
    for(int i = 1;i <= top;i++){
        printf("%c",s[stk[i]]);
    }
    return 0;
}

P2375 [NOI2014] 动物园

题目链接
这道题也是利用next数组的本质,只是如果直接暴力递归的话会被卡掉
可以在getnext函数中先预处理出num
然后再进行一次类似的操作判断 j * 2 <= i得出结果
AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const ll mod = 1e9 + 7;
int n,ne[N],num[N];
ll ans;
char s[N];
int main(){
    int T;scanf("%d",&T);
    while(T --){
        scanf("%s",s);
        n = strlen(s);
        memset(ne,0,sizeof(ne));
        int i,j;
        j = 0;
        num[0] = 0,num[1] = 1;
        for( i = 1;i < n;i ++){
            while(j && s[i] != s[j]) j =ne[j];
            if(s[i] == s[j]) j ++;
            ne[i + 1] = j;
            num[i + 1] = num[j] + 1;
        }
        j = 0,ans = 1;
        for(i = 1;i < n;i ++){
            while(j && s[i] != s[j]) j = ne[j];
            if(s[i] == s[j]) j ++;
            while((j << 1) > (i + 1)) j = ne[j];
            ans = ans * (ll) (num[j] + 1) % mod;
        }
        printf("%lld\n",ans % mod);
    }
    return 0;
}

一个小小的总结:
字符串的题变化多端,但总是离不开那几个最本质的东西!祝AC!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值