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;
}