测试地址:字符串大师
做法: 本题需要用到KMP+构造。
看到这种求字符串循环节之类的东西,就立刻想到KMP。联系这题循环节的定义,我们发现,把条件描述中的前缀改成后缀,答案显然也是一样的。于是我们得出一个大胆的结论:前缀
i
i
i的最短循环节长度等于
i
−
n
e
x
t
i
i-next_i
i−nexti,其中
n
e
x
t
i
next_i
nexti正是KMP算法中的
n
e
x
t
next
next数组。
如果要严格证明的话也不难,令
T
=
i
−
n
e
x
t
i
T=i-next_i
T=i−nexti,那么前缀
n
e
x
t
i
next_i
nexti和前缀
i
i
i的两个长为
T
T
T的后缀是相同的。又因为前缀
n
e
x
t
i
next_i
nexti是前缀
i
i
i的后缀,所以前缀
n
e
x
t
i
−
T
next_i-T
nexti−T和前缀
n
e
x
t
i
next_i
nexti的两个长为
T
T
T的后缀是相同的,以此类推,最后前缀
i
i
i剩下一段长度小于
T
T
T的部分没有得到匹配,说明
T
T
T是前缀
i
i
i的循环节。而要使
T
T
T最小,显然按照KMP算法来做是最优的。
因此直接用KMP算法构造,如果
n
e
x
t
i
>
0
next_i>0
nexti>0,当前位置上的字符应和
n
e
x
t
i
next_i
nexti位置上的字符相同,否则,先求出用
n
e
x
t
next
next匹配会走过的路径,在这条路径上的所有字符都不应该和当前字符匹配,所以我们在当前位置上放能放的最小的字符即可。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
int n,nxt[100010],a[100010];
bool vis[30]={0};
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&nxt[i]);
nxt[i]=i-nxt[i];
}
nxt[0]=0;
for(int i=1;i<=n;i++)
{
memset(vis,0,sizeof(vis));
int last=nxt[i-1];
while(last&&last+1!=nxt[i])
vis[a[last+1]]=1,last=nxt[last];
if (last+1==nxt[i]) a[i]=a[nxt[i]];
else
{
if (last+1<i) vis[a[last+1]]=1;
for(int j=0;j<26;j++)
if (!vis[j]) {a[i]=j;break;}
}
printf("%c",a[i]+'a');
}
return 0;
}