[POI2005]Sza-Template

Byteasar 想在墙上涂一段很长的字符,他为了做这件事从字符的前面一段中截取了一段作为模版. 然后将反复使用这个模版喷涂到相应的位置后就得到了他想要的字符序列.一个字符可以被喷涂很多次,但是一个位置不能喷涂不同的字符.做一个模版很费工夫,所以他想要模版的长度尽量小,求最小长度是多少.拿样例来说 ababbababbabababbabababbababbaba , 模版为前8个字符ababbaba, 喷涂的过程为:

Input
输入一行最多不超过500 000 个最少1个小写字符.

Output
一个长度表示模版最小的长度.

Sample Input
ababbababbabababbabababbababbaba

Sample Output
8

这道题的问题,其实就是从根节点开始往n号节点走一条链,并且每次把当前节点的fail树子树中的所有节点标记,将这些节点数字用一个双向链表来进行维护,统计原串上的最大空隙,如果空隙小于当前节点代表的前缀的长度,就作为答案输出。如果当前节点不满足则取其子结点(位于链上的),进行上述的操作。此时就要将链表中的没用的点删去。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char a[500010];int n,cnt,maxgap,fail[500010],pre[500010],suc[500010],first[500010],ans[500010],tot;
struct edge
{
	//fail树上的边
    int to,next;
}e[500010];
void add(int u,int v)
{
    e[++cnt]=(edge){v,first[u]};first[u]=cnt;
}
void getfail()
{//求next数组(我的代码里叫fail)
    int i,j=0;
    for(i=1;i<n;i++)
	{
        while(j&&(a[i]!=a[j])) 
		     j=fail[j];
        j+=(a[i]==a[j]);
		fail[i+1]=j;
    }
    for(i=1;i<=n;i++) 
	   add(fail[i],i);
}
void del(int x)
{
	//链表删除操作,O(1)
    suc[pre[x]]=suc[x];
    pre[suc[x]]=pre[x];
    maxgap=max(maxgap,suc[x]-pre[x]);
	suc[x]=pre[x]=0;
}
int q[500010];
void bfs(int s,int avoid)
{
	//s的子树中,避开avoid的子树,其余点全部从链表里面删掉
    int u,v,i,head=0,tail=1;q[0]=s;
    while(head<tail)
	{
        u=q[head++];
		if(u==avoid)
		    continue;
        del(u);
        for(i=first[u];~i;i=e[i].next)
		{
            v=e[i].to;
			q[tail++]=v;
        }
    }
}
int main()
{
    memset(first,-1,sizeof(first));
	memset(fail,0,sizeof(fail));
	int i,j;
    scanf("%s",a);
	n=strlen(a);
    getfail();
    for(i=n;i;i=fail[i]) 
	    ans[++tot]=i;
	ans[tot+1]=0;
    for(i=1;i<=n;i++) 
	    pre[i]=i-1,suc[i]=i+1;
    maxgap=1;
    for(i=tot;i>=1;i--)
	{
        bfs(ans[i+1],ans[i]);
        if(maxgap<=ans[i])
		{
            printf("%d",ans[i]);return 0;
        }
    }
}

  

原文地址:https://www.cnblogs.com/cutemush/p/12706316.html