正题
题目链接:https://www.luogu.com.cn/problem/P3426
题目大意
给出一个长度为nnn的字符串sss,求一个长度最小的字符串ttt使得sss所有ttt和ttt匹配的位置能覆盖串sss。
1≤n≤5×1051\leq n\leq 5\times 10^51≤n≤5×105
解题思路
首先答案肯定是原串的一个borderborderborder,设fif_ifi表示前缀s1∼is_{1\sim i}s1∼i的答案。
考虑如何转移,首先fif_ifi至多是iii,然后考虑如果有一个串ttt能够覆盖s1∼nxtis_{1\sim nxt_i}s1∼nxti那么才有可能能覆盖s1∼is_{1\sim i}s1∼i。(因为如果覆盖大于nxtinxt_inxti显然不可能覆盖整个串,不然就至少需要覆盖到s1∼nxtis_{1\sim nxt_i}s1∼nxti)。
考虑什么时候fif_ifi能够取到fnxtif_{nxt_i}fnxti。首先我们可以表示出s1,nxtis_{1,nxt_i}s1,nxti假设我们上次覆盖的位置是j∈[nxti,i]j\in[nxt_i,i]j∈[nxti,i],那么需要有fj=fnxtif_{j}=f_{nxt_i}fj=fnxti(即取到同一个borderborderborder),然后要求j≥i−nxtij\geq i-nxt_ij≥i−nxti(这样就可以用s1,nxtis_{1,nxt_i}s1,nxti覆盖剩下的)。
时间复杂度O(n)O(n)O(n)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5e5+10;
int n,f[N],nxt[N],ls[N];
char s[N];
int main()
{scanf("%s",s+1);n=strlen(s+1);for(int i=2,j=0;i<=n;i++){while(j&&s[j+1]!=s[i])j=nxt[j];j+=(s[i]==s[j+1]);nxt[i]=j;}for(int i=1;i<=n;i++){f[i]=i;if(i-ls[f[nxt[i]]]<=nxt[i])f[i]=f[nxt[i]];ls[f[i]]=i;}printf("%d\n",f[n]);return 0;
}