[BZOJ4947] 字符串大师 - KMP

4974: [Lydsy1708月赛]字符串大师

Time Limit: 1 Sec  Memory Limit: 256 MB
Submit: 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

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 }

 

 
posted @ 2018-06-13 21:13  zZhBr  阅读(272)  评论(0编辑  收藏  举报