bzoj-1535 Sza-Template

141 篇文章 0 订阅
88 篇文章 0 订阅

题意:

给出一个长度为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;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值