题目大意
若将一个串 B B B 复制多遍,然后拼接起来,拼接时可将两个模板串相同的部分叠起来,最后得到一个串 A A A。
我们称串 B B B 是串 A A A 的模板串。
现给定一个字符串,试求出这个字符串的最短模板串,输出这个长度。
解题思路
考虑一个串 A A A 的,他的最短模板串为 B B B,那么具体组成就是这样:
_______________
A
A
A
____-----------
B
B
B
---____--------
B
B
B
------____-----
B
B
B
--------____---
B
B
B
---------____--
B
B
B
-----------____
B
B
B
发现,一个串的模板串只能是这个串的前缀。
那么设 f i f_i fi 为 [ 1 , i ] [1,i] [1,i] 的最小模板串的长度。
设 g i g_i gi 为 长度为 i i i 的模板串最后出现的下标。
那么边界为 f i = i f_i=i fi=i。
考虑转移,如果 g [ f [ n x t [ i ] ] ] < ∣ i − n x t [ i ] ∣ g[f[nxt[i]]]<|i-nxt[i]| g[f[nxt[i]]]<∣i−nxt[i]∣,即两个模板串之间不相交,不可转移( ∣ x ∣ |x| ∣x∣ 为 x x x 的长度)。
如果 g [ f [ n x t [ i ] ] ] > = ∣ i − n x t [ i ] g[f[nxt[i]]]>=|i-nxt[i] g[f[nxt[i]]]>=∣i−nxt[i],即两个模板串之间相交(即 [ 1 , i ] [1,i] [1,i] 与 1 , n x t [ i ] ] 1,nxt[i]] 1,nxt[i]] 的模板串长度相同),可以转移:
f[i] = f[nxt[i]];
其实挺有趣的。
AC CODE
#include <bits/stdc++.h>
using namespace std;
void write(int x)
{
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
#define _ 5000005
int n;
char s[_];
int nxt[_];
int t[_];
int f[_];
signed main()
{
scanf("%s", s + 1);
n = strlen(s + 1);
nxt[0] = -1;
for(int i = 2, j = 0; i <= n; ++i)
{
while(~j && s[j + 1] != s[i]) j = nxt[j];
nxt[i] = ++j;
}
// for(int i = 1; i <= n; ++i) cout << nxt[i] << " ";
// cout << endl;
for(int i = 1; i <= n; ++i)
{
f[i] = i;
if(t[f[nxt[i]]] >= i - nxt[i]) f[i] = f[nxt[i]];
t[f[i]] = i;
}
write(f[n]);
putchar('\n');
return 0;
}