[BZOJ4947] 字符串大师 - KMP
4974: [Lydsy1708月赛]字符串大师
Time Limit: 1 Sec Memory Limit: 256 MBSubmit: 739 Solved: 358
[Submit][Status][Discuss]
Description
一个串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。
Input
第一行包含一个正整数n(1<=n<=100000),表示字符串的长度。
第二行包含n个正整数per_1,per_2,...per_n(1<=per_i<=i),表示每个前缀的最短循环节长度。
输入数据保证至少存在一组可行解。
Output
输出一行一个长度为n的小写字符串S,即某个满足条件的S。
若有多个可行的S,输出字典序最小的那一个。
Sample Input
5
1 2 2 2 5
1 2 2 2 5
Sample Output
ababb
HINT
Source
题解:
我们由最短循环节=i-nxt[i],可以求出每个nxt。
然后贪心地求出答案。
每一位如果它nxt不为0,直接ans[i]=ans[xt[i]];
如果它等于0,那么考虑kmp的过程,沿着nxt[i-1]一直往前跑找到的每一个j,ans[j+1]都不能是现在的ans[i],然后每次取未出现过的字典序最小的字母即可;
Code:
1 #include <iostream> 2 #include <cstdio> 3 #include <map> 4 using namespace std; 5 6 int n; 7 int nxt[100010]; 8 char ans[100010]; 9 10 int main() 11 { 12 scanf("%d", &n); 13 for (register int i = 1 ; i <= n ; i ++) 14 { 15 int x; 16 scanf("%d", &x); 17 nxt[i] = i - x; 18 } 19 nxt[0] = -1; 20 ans[1] = 'a'; 21 22 for (register int i = 2 ; i <= n ; i ++) 23 { 24 if (nxt[i] > 0) ans[i] = ans[nxt[i]]; 25 else 26 { 27 map <char, bool> mp; 28 mp.clear(); 29 int las = nxt[i-1] + 1; 30 while (las) 31 { 32 mp[ans[las]] = 1; 33 las = nxt[las-1] + 1; 34 } 35 for (register int j = 'a' ; j <= 'z' ; j ++) 36 { 37 if (!mp[j]) {ans[i] = j;break;} 38 } 39 } 40 } 41 for (register int i = 1 ; i <= n ; i ++) printf("%c", ans[i]); 42 return 0; 43 }