6555. 【GDOI2020模拟4.11】黑红兔(brr)(SAM小技♂巧)

题目描述


题解

答案<=√n,所以设f[i][j]表示以i结尾长度为j是否存在,做√n次即可O(n√n)

可以把选择的串变成长度依次-1,这样当前选择的串长=选择的第几个串

f[i][j]存在则f[i][j-1]存在,每次删掉当前串末尾字符,若当前串是由上一个串删掉末尾得来的就删掉当前串

所以把f改成最多的个数,二分判断以[i,i+f[i]-2]或[i+1,i+f[i]-1]为前缀的串是否在[i+f[i],n]中出现,并且对应的f+1>=f[i],大于等于是因为可以通过删减变成f[i]-1

假设判断是O(log),这样做是log^2

发现f[i+1]>=f[i]-1,即至少为i删掉一个字符,因此有f[i]<=f[i+1]+1,类似SA一样单调求,总次数为2n

每次询问[i+f[i],n]的串建出来的SAM的parent树的子树,修改就把整个后缀丢进去

SAM小技♂巧:

如何O(log)寻找到一个串的子串对应节点:在parent上倍增,最后一个len>=|S|就是

还有这道题并不需要考虑一个节点对应的多个串之间的关系,因为丢进去的是整个后缀,是主链上的点上的最长串,因此不需要考虑询问的串在SAM某个节点上的具体大小关系,直接询问即可

SAM的注意事项(复习):

①如果复制节点则要把后面的所有连向该节点的边修改,这样的点在parent树上是连续的一段,手玩一下可得

②节点可能小->大,所以倍增要在dfs上预处理

code

#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
#define ll long long
#define file
using namespace std;

int tr[4000001],a[500001],b[1000001][26],fa[1000001][20],len[1000001],num[1000001];
int A[1000001][2],ls[1000001],bg[1000001],ed[1000001],f[1000001],n,i,j,k,l,ans,N,Len;
char st[500001];
bool bz;

void New(int t,int x) {++N;memcpy(b[N],b[b[t][x]],sizeof(b[N]));b[t][x]=N;fa[N][0]=t;len[N]=len[t]+1;}
void tNew(int x,int y) {++Len;A[Len][0]=y;A[Len][1]=ls[x];ls[x]=Len;}
void work(int t)
{
	int i;
	fo(i,1,19) fa[t][i]=fa[fa[t][i-1]][i-1];
}
void sam()
{
	int i,j,k,l,ls;
	
	N=ls=1;
	fo(i,1,n)
	{
		j=fa[ls][0];New(ls,a[i]);ls=N;num[i]=ls;
		while (j && !b[j][a[i]])
		{
			b[j][a[i]]=ls;
			j=fa[j][0];
		}
		if (!j) fa[ls][0]=1;
		else
		{
			if (len[j]+1==len[b[j][a[i]]]) fa[ls][0]=b[j][a[i]];
			else
			{
				k=b[j][a[i]],New(j,a[i]);fa[N][0]=fa[k][0],fa[k][0]=fa[ls][0]=N;
				j=fa[j][0];
				while (b[j][a[i]]==k) b[j][a[i]]=N,j=fa[j][0];
			}
		}
	}
	fo(i,2,N) tNew(fa[i][0],i);
}

int jump(int t,int x)
{
	int i;
	fd(i,19,0) if (len[fa[t][i]]>=x) t=fa[t][i];
	return t;
}

void dfs(int Fa,int t)
{
	int i;
	work(t);
	
	bg[t]=++j;
	for (i=ls[t]; i; i=A[i][1]) if (A[i][0]!=Fa) dfs(t,A[i][0]);
	ed[t]=j;
}

void change(int t,int l,int r,int x,int s)
{
	int mid=(l+r)/2;
	
	tr[t]=max(tr[t],s);
	if (l==r) return;
	
	if (x<=mid) change(t*2,l,mid,x,s);
	else
	change(t*2+1,mid+1,r,x,s);
}
int find(int t,int l,int r,int x,int y)
{
	int s,ans=0,mid=(l+r)/2;
	
	if (x<=l && r<=y) return tr[t];
	
	if (x<=mid) s=find(t*2,l,mid,x,y),ans=max(ans,s);
	if (mid<y) s=find(t*2+1,mid+1,r,x,y),ans=max(ans,s);
	
	return ans;
}

bool pd(int x,int y)
{
	int i=jump(num[y],y-x+1);
	return find(1,1,N,bg[i],ed[i])>=y-x+1;
}

int main()
{
	freopen("brr.in","r",stdin);
	#ifdef file
	freopen("brr.out","w",stdout);
	#endif
	
	scanf("%s",st);n=strlen(st);
	fo(i,1,n) a[n-i+1]=st[i-1]-'a',bz=(a[n-i+1]==a[n])?bz:1;
	if (!bz)
	{
		while (n>ans) ++ans,n-=ans;
		printf("%d\n",ans);
		return 0;
	}
	
	sam();
	j=0;dfs(0,1);
	
	fo(i,1,n)
	{
		f[i]=f[i-1]+1;
		while (f[i]>1 && !pd(i-f[i]+1,i-1) && !pd(i-f[i]+2,i)) change(1,1,N,bg[num[i-f[i]+1]],f[i-f[i]+1]),--f[i];
		
		ans=max(ans,f[i]);
	}
	
	printf("%d\n",ans);
	
	fclose(stdin);
	fclose(stdout);
	
	return 0;
}
posted @ 2020-04-13 16:10  gmh77  阅读(209)  评论(0编辑  收藏  举报