【洛谷3426】[POI2005] SZA-Template(KMP)

点此看题面

  • 有一个长度为(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]]
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3426.html