【洛谷3426】[POI2005] SZA-Template(KMP)
- 有一个长度为\(n\)的文本串,求一个最小长度的模式串可以打印出这个文本串。
- 打印是指文本串一个位置可以匹配多次,但每个位置至少要匹配一次。
- \(n\le5\times10^5\)
\(KMP\)+\(DP\)
考虑到这个模式串必然是文本串的一个前缀,因此知道长度就可以推断出其内容。
所以记录\(f_i\)表示打印出文本串前\(i\)个字符所需的最小长度模式串。
实际上,这个模式串同时也必然是文本串的一个后缀,因此我们\(KMP\)求出\(nxt\)数组,那么\(f_i\)实际上只有两种可能的取值:\(i\)或者\(f_{nxt[i]}\)。
显然\(i>f_{nxt[i]}\),因此我们只需验证\(f_{nxt[i]}\)是否可行,那就是要判断是否存在\(j\in[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\n",f[n]),0;//简单DP,只需检验f[nxt[i]]
}
待到再迷茫时回头望,所有脚印会发出光芒