BZOJ4974【Lydsy201708月赛】字符串大师

Problem

【Lydsy1708月赛】字符串大师

Time  Limit:  1  Sec\mathrm{Time\;Limit:\;1\;Sec}
Memory  Limit:  256  MB\mathrm{Memory\;Limit:\;256\;MB}

Description

一个串TTSS的循环节,当且仅当存在正整数kk,使得SSTT重复kk次的前缀,比如abcdabcdabcdab的循环节。
给定一个长度为nn的仅由小写字符构成的字符串SS,请对于每个k  (1kn)k\;(1\le k\le n),求出SS长度为kk的前缀的最短循环节的长度periper_i
字符串大师Q\mathrm{小Q}觉得这个问题过于简单,于是花了一分钟将其AC\mathrm{AC}了,他想检验你是否也是字符串大师。
Q\mathrm{小Q}告诉你nn以及per1,per2,,pernper_1,per_2,\cdots,per_n,请找到一个长度为nn的小写字符串SS,使得SS能对应上perper

Input

第一行包含一个正整数n  (1n105)n\;(1\le n\le10^5),表示字符串的长度。
第二行包含nn个正整数per1,per2,,pern  (1perii)per_1,per_2,\cdots,per_n\;(1\le per_i\le i),表示每个前缀的最短循环节长度。
输入数据保证至少存在一组可行解。

Output

输出一行一个长度为nn的小写字符串SS,即某个满足条件的SS
若有多个可行的SS,输出字典序最小的那一个。

Sample Input

1
2
5
1 2 2 2 5

Sample Output

1
ababb

Source

Claris原创,本OJ\mathrm{OJ}版权所有,翻版必究

标签:KMP 贪心 构造

Solution

KMP\mathrm{KMP}好题。

\bigstar本文中所有数组和字符串下标从00开始。

首先有一个结论:nxti=iperinxt_i=i-per_i
证明:
对于字符串PP,其最短循环节为RR,除去循环节后多余的部分为QQ,如图所示。

![1.PNG](https://i.loli.net/2018/05/02/5ae96516e82ca.png)

那么再在上面接一个RR,一定可以包含PP,于是可以知道QQ一定是RR的前缀,所以有下图:

![2.PNG](https://i.loli.net/2018/05/02/5ae96516e8d6d.png)

PP末尾循环节长度那么长去掉,得到P1P_1,将PP第一个循环节去掉,得到P2P_2,发现两者是相同的(如下图)。而这显然是PPboarderboarder,所以P1P_1的末尾位置为npern-per,即nxt[n]=npernxt[n]=n-per

![3.PNG](https://i.loli.net/2018/05/02/5ae965170167b.png)

这样根据给出的perper可以将nxtnxt数组处理出来。
从前往后构造,对于位置ii

  • nxti1nxt_i\ne-1,一定有s[i]=s[nxti]s[i]=s[nxt_i],可以直接赋值
  • nxti=1nxt_i=-1,那么在计算nxtnxt的过程中,即将这个串与自己做匹配的时候,不断根据nxtnxt向前跳到的位置一定不会和当前位置匹配,否则nxti=最先能匹配的位置nxt_i=最先能匹配的位置。于是将能向前跳到的位置上的字符存下来,找一个最小的没有跳到过的字符作为这一位置的字符

如此贪心构造即可得到最优解。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <bits/stdc++.h>
#define MAX_N 100000
using namespace std;
template <class T> inline void read(T &x) {
x = 0; int c = getchar(), f = 1;
for (; !isdigit(c); c = getchar()) if (c == 45) f = -1;
for (; isdigit(c); c = getchar()) (x *= 10) += f*(c-'0');
}
int n, nxt[MAX_N+5], s[MAX_N+5]; bool mrk[26];
int main() {
read(n);
for (int i = 0; i < n; i++) read(nxt[i]), nxt[i] = i-nxt[i];
for (int p = 1; p < n; p++) {
if (~nxt[p]) s[p] = s[nxt[p]];
else {
memset(mrk, false, sizeof mrk);
for (int q = nxt[p-1]; ~q; q = nxt[q])
mrk[s[q+1]] = true;
for (int c = 1; c < 26; c++)
if (!mrk[c]) {s[p] = c; break;}
}
}
for (int i = 0; i < n; i++) printf("%c", 'a'+s[i]);
return 0;
}