[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<L≤106。
按照题意,我们要求的是
s
2
s_2
s2的最短长度,也就是
s
1
s_1
s1最小的循环字串。
这里实际上有一个结论:答案就是
n
−
p
m
t
[
n
−
1
]
n-pmt[n-1]
n−pmt[n−1]。(可能有的结论是
n
−
p
m
t
[
n
]
n-pmt[n]
n−pmt[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<=5,1<=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[n−1]即可,那么
n
−
p
m
t
[
n
−
1
]
n-pmt[n-1]
n−pmt[n−1]就是答案了。
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;
}