题目描述
给你一个字符串 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
思路
非常的尴尬,这个题我第一遍做AC了,但是看题解发现我想的非常的不逻辑,且非常的扯。
非常奇妙的思维……orz
他这个题就是要求最小的周期。既然是字符串的匹配题所以和KMP是挂钩的这个样子。
直接暴搜能过1/10好像?
然后其实到这里怎么KMP还没有思路。
但是反正知道KMP,所以就把整个串的ne数组算出来了orz
然后,在KMP模板题里写到的:如果他能匹配,那么会用前一个的ne数值+1。
个人解题思路
于是有了一个结论对了但是过程错了的代码:
如果在某一位置直到末尾是从1开始的连续自然数,说明这个位置到末尾这一段字符串应该是均可以与此位置之前的字符串进行匹配的,那么,相当于从1开始到末尾都是“废数”,于是直接n-next[n]出结果,对,它AC了。但是这段话是有大问题的。
反例:如果母串的开头是母串的结尾,例如AAAACAAACA,就会发生在某一位置“顿一下”的修正。这就很神奇。
简单证了一下,发现修正的操作仅会发生在一个周期与第二个周期之间,且修正后的数列可以看作是除了第一个周期外,多余数字的数量。即不论是否被修正,修正后的next依然可以从某个位置开始,回归到我们希望看到的那个分割方式(即答案)
接下来给出数学证明:
证明:
最长前缀=最长后缀的坐标:
最长前缀:1~n-next[n]
最长后缀:next[n]+1~n
整个串的为
L
0
=
α
0
T
+
β
L_0=\alpha_0 T+\beta
L0=α0T+β其中,
T
T
T是最小周期的字符串,
α
\alpha
α是前缀包含的周期的数量,
β
\beta
β是不满一周期的字符串。如果
β
\beta
β存在,那么显然后缀肯定含有
β
\beta
β。
假设最长后缀
L
=
α
T
+
β
L=\alpha T+\beta
L=αT+β,
那么,最长前缀
L
l
a
t
t
e
r
=
L
=
α
T
+
β
L_{latter}=L=\alpha T+\beta
Llatter=L=αT+β
那么
n
−
n
e
x
t
[
n
]
=
(
α
0
−
α
)
∣
T
∣
n-next[n]=(\alpha_0 - \alpha)|T|
n−next[n]=(α0−α)∣T∣
假设
α
0
−
α
≠
1
\alpha_0 -\alpha \neq 1
α0−α=1则最长前缀=最长后缀
≠
α
T
+
β
\neq \alpha T+\beta
=αT+β(是后缀/前缀,而非最长),假设不成立,所以
n
−
n
e
x
t
[
n
]
=
∣
T
∣
n-next[n]=|T|
n−next[n]=∣T∣
证明可能不严谨,望指正, 仅供参考
喜闻乐见
#include<iostream>
#include<cstring>
using namespace std;
const int N=1e6+100;
char str[N];
int ne[N];
int main(){
int n;
cin>>n;
for(int i=1; i<=n; ++i)cin>>str[i];
memset(ne, 0, sizeof ne);
for(int i=2, j=0; i<=n; ++i){
while(j && str[i]!=str[j+1]){
j=ne[j];
}
if(str[i]==str[j+1])++j;
ne[i]=j;
if(ne[i]==1)cout<<i<<' '<<str[i]<<endl;
}
cout<<n-ne[n]<<endl;
return 0;
}