- 有一个长度为(n)的文本串,求一个最小长度的模式串可以打印出这个文本串。
- 打印是指文本串一个位置可以匹配多次,但每个位置至少要匹配一次。
- (nle5 imes10^5)
(KMP)+(DP)
考虑到这个模式串必然是文本串的一个前缀,因此知道长度就可以推断出其内容。
所以记录(f_i)表示打印出文本串前(i)个字符所需的最小长度模式串。
实际上,这个模式串同时也必然是文本串的一个后缀,因此我们(KMP)求出(nxt)数组,那么(f_i)实际上只有两种可能的取值:(i)或者(f_{nxt[i]})。
显然(i>f_{nxt[i]}),因此我们只需验证(f_{nxt[i]})是否可行,那就是要判断是否存在(jin[i-f_{nxt[i]},i))满足(f_j=f_{nxt[i]}),这样一来就可以在(j)的基础上再匹配上一个(f_{nxt[i]})得到(i)。
由于我们是从左往右(DP)的,肯定满足(j<i),因此这等价于判断最大的满足(f_j=f_{nxt[i]})的(j)是否大于等于(i-f_{nxt[i]})。
只需开个(g_x)维护一下满足(f_i=x)的最后出现的(i)即可。
代码:(O(n))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 500000
using namespace std;
int n,nxt[N+5],f[N+5],g[N+5];char s[N+5];
int main()
{
RI i,j,x;scanf("%s",s+1),n=strlen(s+1);
for(i=2,j=0;i<=n;s[j+1]==s[i]&&++j,nxt[i++]=j) W(j&&s[j+1]^s[i]) j=nxt[j];//KMP求nxt数组
for(i=1;i<=n;++i) x=f[nxt[i]],g[f[i]=g[x]>=i-x?x:i]=i;return printf("%d
",f[n]),0;//简单DP,只需检验f[nxt[i]]
}