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

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

题目链接:BZOJ 4974 [Lydsy1708月赛]字符串大师 (挂)

算法标签: 字符串KMP

题目

题目描述

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

小Q告诉你(n)以及(per_1,per_2,...,per_n),请找到一个长度为(n)的小写字符串(S),使得(S)能对应上(per)

输入格式

第一行包含一个正整数(n)((1le nle 100000)),表示字符串的长度。

第二行包含(n)个正整数(per_1,per_2,...per_n(1le per_ile i)),表示每个前缀的最短循环节长度。

输入数据保证至少存在一组可行解。

输出格式

输出一行一个长度为(n)的小写字符串(S),即某个满足条件的(S)

若有多个可行的(S),输出字典序最小的那一个。

输入输出样例

输入 #1

5
1 2 2 2 5

输出 #1

ababb

题解:

题解内部分思路和叙述参考https://www.cnblogs.com/GXZlegend/p/7559669.html

一堆废话:

很久没有水题解了,刚好这几天模拟赛,活跃一下博客。

算法:KMP

说道这个算法,如果你深入学习过KMP算法,那么应该会做过这一道题:

POJ-2406 Power Strings

通过研究这道题,我们可以发现对于KMP中(nxt[~])数组的一个性质:

  • (S)的最小循环子串(最小循环节)长度为(len - nxt[len])

这道这个之后再回来重新看这道题,我们会发现,这里用到了这个性质,只不过是你逆序的,所以我们就可以通过所给的循环节长度直接得到(nxt[1] sim nxt[len])

for (int i = 1, x; i <= n; i ++ ) {
	scanf("%d", &x);
	nxt[i] = i - x;
}

这样的话我们就直接按照KMP的过程进行逆序模拟:

  • 如果(nxt[i] eq 0),那么就可以在(i)之前的部分中找到(s[nxt[i]])

  • 如果(nxt[i] = 0),根据KMP的过程,对于一个匹配位置,如果它的下一个字符和它不相等,那么我们就跳到当前的(nxt)位置,一直到(nxt=-1)为止,之后当前位置的(nxt[i])就等于匹配到的(nxt + 1),这样反复而处理出(nxt[~])数组。

    那么如果当前状态是(nxt[i] = 0)的完全失配状态,每一次匹配到的位置上的字符都应该与当前字符失配。这样我们可以记录所有在匹配位置的字符,找到第一个没有出现的字符放到当前位置上即可。

Tips:对于记录所有出现过的匹配位置字符,我们可以选择(STL-set)直接去重和维护有序,也可以通过按照每一位的间戳进行标记,再扫26个字母找到第一个没有出现过的即可。(由于题目保证有解)

for (int j = nxt[i - 1]; ~j; j = nxt[j]) {
	vis[str[j + 1] - 'a'] = i;
}
for (int j = 0; j < 26; j ++ ) {
	if (vis[j] != i) {
		tmp = j;
		break ;
	}
}
str[i] = tmp + 'a';

AC代码

#include <bits/stdc++.h>

using namespace std;

#define setI(x) freopen(x".in", "r", stdin);

#define setO(x) freopen(x".out", "w", stdout);

#define setIO(x) setI(x) setO(x);

int nxt[100010];

int n, x, tmp;

int vis[100010];

char str[100010];

int main() {
// setIO("meaning")
	scanf("%d", &n);
	nxt[0] = -1;
	for (int i = 1; i <= n; i ++ ) {
		scanf("%d", &x);
		nxt[i] = i - x;
		if (nxt[i]) {
			str[i] = str[nxt[i]];
			continue ;
		}
		for (int j = nxt[i - 1]; ~j; j = nxt[j]) {
			vis[str[j + 1] - 'a'] = i;
		}
		for (int j = 0; j < 26; j ++ ) {
			if (vis[j] != i) {
				tmp = j;
				break ;
			}
		}
		str[i] = tmp + 'a';
	}
	printf("%s
", str + 1);
	return 0;
}
原文地址:https://www.cnblogs.com/littleseven777/p/11841697.html