[KMP]Luogu P4391

文章讲述了如何在给定一个字符串s1的情况下,找到其不确定的字符串s2的最短长度,即s1的最小循环字串。方法是利用最长公共前缀的思想,通过计算pmt数组来确定s2的边界。
摘要由CSDN通过智能技术生成

[BOI2009] Radio Transmission 无线传输

题目描述

给你一个字符串 s 1 s_1 s1,它是由某个字符串 s 2 s_2 s2 不断自我连接形成的(保证至少重复 2 2 2 次)。但是字符串 s 2 s_2 s2
是不确定的,现在只想知道它的最短长度是多少。

输入格式

第一行一个整数 L L L,表示给出字符串的长度。

第二行给出字符串 s 1 s_1 s1 的一个子串,全由小写字母组成。

输出格式

仅一行,表示 s 2 s_2 s2 的最短长度。

样例 #1

样例输入 #1

8 cabcabca

样例输出 #1

3

提示

样例输入输出 1 解释 对于样例,我们可以利用 abc \texttt{abc} abc 不断自我连接得到 abcabcabcabc \texttt{abcabcabcabc} abcabcabcabc,读入的 cabcabca \texttt{cabcabca} cabcabca,是它的子串。
规模与约定 对于全部的测试点,保证 1 < L ≤ 1 0 6 1 < L \le 10^6 1<L106

按照题意,我们要求的是 s 2 s_2 s2的最短长度,也就是 s 1 s_1 s1最小的循环字串。
这里实际上有一个结论:答案就是 n − p m t [ n − 1 ] n-pmt[n-1] npmt[n1]。(可能有的结论是 n − p m t [ n ] n-pmt[n] npmt[n],只是因为下标是从1开始)
具体证明可以看这篇博客:(写得很清楚了)
大佬的详解
下面贴上代码:

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int MAXN=1e6+10;
int pmt[MAXN];
int main(){
    int n;
    cin>>n;
    string s;
    cin>>s;
    for(int i=1,j=0;i<n;i++){
        while(j&&s[i]!=s[j])j=pmt[j-1];
        if(s[i]==s[j])j++;
        pmt[i]=j;
    }
    cout<<n-pmt[n-1];
    return 0;
}

补充证明

在这里插入图片描述
上面的前后缀是 m a x ( b o r d e r ) max(border) max(border)。不妨记上面的前缀为 s 1 s_1 s1,下面的后缀为 s 2 s_2 s2
我们可以发现在这里插入图片描述
箭头联系起来的各部分是相等的,也就是 s 1 [ 1 ] = s 2 [ 2 ] 、 s 1 [ 2 ] = s 2 [ 3 ] 、 s 1 [ 3 ] = s 2 [ 4 ] ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ s_1[1]=s_2[2]、s_1[2]=s_2[3]、s_1[3]=s_2[4]······ s1[1]=s2[2]s1[2]=s2[3]s1[3]=s2[4]⋅⋅⋅⋅⋅⋅,由此类推。
同时,又因为 s 1 [ 1 ] = s 2 [ 1 ] 、 s 1 [ 2 ] = s 2 [ 2 ] 、 s 1 [ 3 ] = s 2 [ 3 ] ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ s_1[1]=s_2[1]、s_1[2]=s_2[2]、s_1[3]=s_2[3]······ s1[1]=s2[1]s1[2]=s2[2]s1[3]=s2[3]⋅⋅⋅⋅⋅⋅,将两者联系起来,我们可以得到:
s 1 [ i ] = s 2 [ j ] ∣ 1 < = i < = 5 , 1 < = j < = 5 s_1[i]=s_2[j]|1<=i<=5,1<=j<=5 s1[i]=s2[j]∣1<=i<=51<=j<=5
那有没有可能:更一般的情况是: s 1 s_1 s1红色部分的后面加上一小段普通的字符串 t t t
我们可以分析一下:实际上这是可能的。我们可以结合样例进行分析。(这里不写了,样例的解释很清楚,可以认为:最后的循环节被强行切割了一部分。)
所以我们总结一下上面的内容:红色部分的字符串就是我们要求的最小循环节了。
所以问题转化为:求 m a x ( b o r d e r ) max(border) max(border)
只需要求出 p m t [ n − 1 ] pmt[n-1] pmt[n1]即可,那么 n − p m t [ n − 1 ] n-pmt[n-1] npmt[n1]就是答案了。

AC代码:

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int MAXN=1e6+10;
int pmt[MAXN];
int main(){
    int n;
    cin>>n;
    string s;
    cin>>s;
    for(int i=1,j=0;i<n;i++){
        while(j&&s[i]!=s[j])j=pmt[j-1];
        if(s[i]==s[j])j++;
        pmt[i]=j;
    }
    cout<<n-pmt[n-1];
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值