【BZOJ4974】字符串大师(LYDSY1708)-KMP+构造

测试地址:字符串大师
做法: 本题需要用到KMP+构造。
看到这种求字符串循环节之类的东西,就立刻想到KMP。联系这题循环节的定义,我们发现,把条件描述中的前缀改成后缀,答案显然也是一样的。于是我们得出一个大胆的结论:前缀ii的最短循环节长度等于inextii-next_i,其中nextinext_i正是KMP算法中的nextnext数组。
如果要严格证明的话也不难,令T=inextiT=i-next_i,那么前缀nextinext_i和前缀ii的两个长为TT的后缀是相同的。又因为前缀nextinext_i是前缀ii的后缀,所以前缀nextiTnext_i-T和前缀nextinext_i的两个长为TT的后缀是相同的,以此类推,最后前缀ii剩下一段长度小于TT的部分没有得到匹配,说明TT是前缀ii的循环节。而要使TT最小,显然按照KMP算法来做是最优的。
因此直接用KMP算法构造,如果nexti>0next_i>0,当前位置上的字符应和nextinext_i位置上的字符相同,否则,先求出用nextnext匹配会走过的路径,在这条路径上的所有字符都不应该和当前字符匹配,所以我们在当前位置上放能放的最小的字符即可。
以下是本人代码:

#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;
}
posted @ 2018-09-29 20:27  Maxwei_wzj  阅读(112)  评论(0编辑  收藏  举报