题意:
给出一个长度为n的字符串,求用它的一个子串作为模板能粘贴出整个字符串的最小长度;
n<=500000;
题解:
首先我们可以知道,这个模板串一定是既为原串的一个前缀也为它的一个后缀的,否则并不能拼出来这个字符串
那么利用KMP或AC自动机构建出fail树,答案只可能出现在树中(root,n]这条路径上;
那么问题就是:枚举树上的每一个点(也是串中的一个前缀),判断它是否能覆盖整个字符串,能的话更新答案;
这步转化之后复杂度没有改变,但是fail树上还有另一个性质:一个点代表的字符串是这个点的子树中所有点的后缀,且拥有这个后缀的所有串都在子树内;
因此我们只要维护这个子树中相邻两个匹配点的最大间隔,判断与原串的大小关系就可以了;
从根向下枚举,利用子树集合单调性,用一个链表维护子树元素即可;
每个元素只会被删除一次,所以总复杂度是O(n)的;
%%%JCVB
顺便我又忘了KMP咋写了啊啊啊
代码:
#include<stdio.h> #include<string.h> #include<algorithm> #define N 510000 using namespace std; char str[N]; int fail[N]; int next[N],to[N],head[N],ce; int son[N],L[N],R[N]; void getfail(int n) { int i=1,j=0; fail[1]=0; while(i<=n) { if(j==0||str[i]==str[j]) { i++,j++; fail[i]=j; } else j=fail[j]; } } void add(int x,int y) { to[++ce]=y; next[ce]=head[x]; head[x]=ce; } void dfs(int x,int &len) { R[L[x]]=R[x]; L[R[x]]=L[x]; len=max(R[x]-L[x],len); L[x]=0x3f3f3f3f; for(int i=head[x];i;i=next[i]) dfs(to[i],len); } int main() { int n,m,i,j,k,ans,len; scanf("%s",str+1); n=strlen(str+1); getfail(n); for(i=1;i<=n;i++) fail[i]=fail[i+1]-1,add(fail[i],i); i=n; while(fail[i]) { son[fail[i]]=i; i=fail[i]; } son[0]=i; for(i=1;i<=n;i++) { L[i]=i-1; R[i]=i+1; } for(i=0,ans=n,len=1;i!=n;) { for(j=head[i];j;j=next[j]) { if(to[j]!=son[i]) { dfs(to[j],len); } } i=son[i]; if(len<=i) ans=min(ans,i); } printf("%d\n",ans); return 0; }
原文:http://blog.csdn.net/ww140142/article/details/49513031