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