zoukankan      html  css  js  c++  java
  • 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
    ",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
    ",ans);
    	
    	fclose(stdin);
    	fclose(stdout);
    	
    	return 0;
    }
    
  • 相关阅读:
    机会的三种境界
    常用“快”捷键
    心路历程
    中兴笔试及答案
    浅谈oracle中row_number() over()分析函数用法
    IE的F12开发人员工具不显示问题
    1002.A + B Problem II --大数问题
    6470.count --快速矩阵幂
    4151.电影--贪心
    3070.斐波拉契数列--快速幂
  • 原文地址:https://www.cnblogs.com/gmh77/p/12692024.html
Copyright © 2011-2022 走看看