现在同学们把大多数作业都做完了,但是却被最后一个题给难住了。
一般地,对于一个字符串S,和S中第k个字符,定义子串T=S(i..j)为一个关于k的识别子串,当且仅当
1、i<=k<=j。
2、T在S中只出现一次。
比如,对于banana的第5个字符,“nana”,“anan”,“anana”,“nan”,“banan”和“banana”都是关于它的识别子串。
自然,识别子串越短越好(太长了也就失去意义了),现在请你计算出对于一个字符串S,关于S的每一位的最短识别子串分别有多长。
一行,一个长度为L的字符串S,S只包含小写字母。
L行,每行1个整数,第i行的数表示关于S的第i个元素的最短识别子串有多长。
agoodcookcooksgoodfood
1
2
3
3
2
2
3
3
2
2
3
3
2
1
2
3
3
2
1
2
3
4
第一个点 L=100
第二个点 L=1000
第三个点 L=5000
第四个点到第十个点 L=100000
首先算出后缀数组,然后算那个 height 数组。
现在定义 extend[i] = i + max(height[rank[i]], height[rank[i]+1]),表示 extend[i]是从 i 开始的识别子串至少要延伸到哪里,即当且仅当 j>=extend[i]时,字串 T=S(i..j)在 S 中只出现一次。
所以,对于一个目标位置 k 和识别子串的起始位置 i,识别子串的终止位置 j 为max(extend[i],k)。
现在,我们用枚举识别子串的起始位置 i 去更新所有答案,下面分情况讨论。
时间复杂度 O(nlogn)
空间复杂度 O(n)
#include
#include
#include
using namespace std;
const int maxn=100077*3,inf=0x3f3f3f3f;
int b[maxn],c[maxn],d[maxn],s[maxn],rank[maxn*2],height[maxn],SA[maxn],mn,wz,n;
char st[maxn*5];
struct E
{
int left,right;
}e[maxn];
void get_SA(int n,int m)
{
for (int i=1;i<=n;i++) b[s[i]]++;
for (int i=1;i<=m;i++) b[i]+=b[i-1];
for (int i=n;i>=1;i--) c[b[s[i]]--]=i;
int x=0,j=1;
for (int i=1;i<=n;i++)
{
if (s[c[i]]!=s[c[i-1]]) x++;
rank[c[i]]=x;
}
while (j<=n)
{
for (int i=1;i<=n;i++) b[i]=0;
for (int i=1;i<=n;i++) b[rank[i+j]]++;
for (int i=1;i<=n;i++) b[i]+=b[i-1];
for (int i=n;i>=1;i--) c[b[rank[i+j]]--]=i;
for (int i=1;i<=n;i++) b[i]=0;
for (int i=1;i<=n;i++) b[rank[i]]++;
for (int i=1;i<=n;i++) b[i]+=b[i-1];
for (int i=n;i>=1;i--) d[b[rank[c[i]]]--]=c[i];
x=0;
for (int i=1;i<=n;i++)
{
if (rank[d[i]]!=rank[d[i-1]]||rank[d[i]]==rank[d[i-1]]&&rank[d[i]+j]!=rank[d[i-1]+j]) x++;
c[d[i]]=x;
}
for (int i=1;i<=n;i++) rank[i]=c[i];
if (x==n) break;
j=j*2;
}
}
void get_height(int n)
{
int x=0;
for (int i=1;i<=n;i++) SA[rank[i]]=i;
for (int i=1;i<=n;i++)
{
if (x!=0) x--;
int j=SA[rank[i]-1];
while (i+x<=n&&j+x<=n&&s[i+x]==s[j+x]) x++;
height[rank[i]]=x;
}
height[1]=0;
}
void build(int x,int l,int r)
{
e[x].left=-inf; e[x].right=inf;
if (l==r) return;
int mid=(l+r)/2;
build(x*2,l,mid); build(x*2+1,mid+1,r);
}
void add1(int id,int l,int r,int x,int y,int z)
{
if (l==x&&r==y) { e[id].right=min(e[id].right,z); return; }
int mid=(l+r)/2;
if (y<=mid) add1(id*2,l,mid,x,y,z);
else if (x>mid) add1(id*2+1,mid+1,r,x,y,z);
else add1(id*2,l,mid,x,mid,z),add1(id*2+1,mid+1,r,mid+1,y,z);
}
void add2(int id,int l,int r,int x,int y,int z)
{
if (l==x&&r==y) { e[id].left=max(e[id].left,z); return; }
int mid=(l+r)/2;
if (y<=mid) add2(id*2,l,mid,x,y,z);
else if (x>mid) add2(id*2+1,mid+1,r,x,y,z);
else add2(id*2,l,mid,x,mid,z),add2(id*2+1,mid+1,r,mid+1,y,z);
}
void query(int id,int l,int r,int x)
{
mn=min(mn,e[id].right); wz=max(wz,e[id].left);
if (l==r) return;
int mid=(l+r)/2;
if (x<=mid) query(id*2,l,mid,x);
else query(id*2+1,mid+1,r,x);
}
int main()
{
scanf("%s",st+1); n=strlen(st+1);
for(int i=1; i<=n; i++) s[i]=st[i]-96;
get_SA(n,30);
get_height(n);
build(1,1,n);
for (int i=1;i<=n;i++)
{
int x=SA[i],l=max(height[i],height[i+1]);
if (l==n-x+1) continue;
add1(1,1,n,x,x+l,l+1);
if (x+l+1<=n) add2(1,1,n,x+l+1,n,x);
}
for (int i=1;i<=n;i++)
{
mn=inf; wz=-inf;
query(1,1,n,i);
printf("%d\n",min(mn,i-wz+1));
}
}