BZOJ4974 八月月赛 Problem D 字符串大师 KMP

欢迎访问~原文出处——博客园-zhouzhendong

去博客园看该题解


题目传送门 - BZOJ4974 - 八月月赛 Problem D


题意概括

  一个串T是S的循环节,当且仅当存在正整数k,使得S是T^k(即T重复k次)的前缀,比如abcd是abcdabcdab的循环节。给定一个长度为n的仅由小写字符构成的字符串S,请对于每个k(1<=k<=n),求出S长度为k的前缀的最短循环节的长度per_i。字符串大师小Q觉得这个问题过于简单,于是花了一分钟将其AC了,他想检验你是否也是字符串大师。小Q告诉你n以及per_1,per_2,...,per_n,请找到一个长度为n的小写字符串S,使得S能对应上per。


 题解

  

  详细操作见代码。


代码

#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <vector>
using namespace std;
const int N=100000+5;
int n,Next[N],same[N],fa[N];
vector <int> diff[N],d2[N];
char s[N];
int getf(int k){
	return fa[k]==k?k:fa[k]=getf(fa[k]);
}
int main(){
	scanf("%d",&n);
	for (int i=1;i<=n;i++){
		fa[i]=i;
		diff[i].clear();
		d2[i].clear();
	}
	for (int i=1,x;i<=n;i++){
		scanf("%d",&x);
		Next[i]=i-x;
		if (i==1)
			continue;
		int k;
		for (k=i-1;k>0;k=Next[k])
			if (Next[k]+1==Next[i])
				break;
			else
				diff[i].push_back(Next[k]+1),diff[Next[k]+1].push_back(i);
		if (Next[i]>0){
			same[i]=Next[k]+1;
			fa[getf(i)]=getf(Next[k]+1);
		}
		else
			same[i]=-1;
	}
	for (int i=1;i<=n;i++){
		int f=getf(i);
		for (int j=0;j<diff[i].size();j++)
			d2[f].push_back(diff[i][j]);
	}
	s[1]='a';
	for (int i=2;i<=n;i++){
		if (same[i]!=-1){
			s[i]=s[same[i]];
			continue;
		}
		bool f[26];
		memset(f,0,sizeof f);
		int bh=getf(i);
		for (int j=0;j<d2[bh].size();j++){
			if (d2[bh][j]>=i)
				continue;
			f[s[d2[bh][j]]-'a']=1;
		}
		int j;
		for (j=0;j<26;j++)
			if (!f[j])
				break;
		s[i]=j+'a';
	}
	for (int i=1;i<=n;i++)
		printf("%c",s[i]);
	return 0;
}

  

 

posted @ 2017-08-24 15:19  zzd233  阅读(575)  评论(0编辑  收藏  举报