【题解】洛谷 P4051 [JSOI2007] 字符加密 [后缀数组]
没有学过后缀数组可以看这个:【详细注释 | 字符串算法集合 2】后缀自动机 SAM & 后缀数组 SA-CSDN博客
P4051 [JSOI2007] 字符加密 - 洛谷 (luogu.com.cn)
说实话,挺板的。
复制一份字符串在后面,成一个新的字符串,把每个后缀求 sa 后缀数组。
最后按排名输出就好了。
需要注意的是这题要开四倍,因为本来 sa 模板 + k 那里就得开两倍,这题还得 * 2。
#include<bits/stdc++.h>
using namespace std;const int N = 4e5 + 10;
char s[N];
int n, m;
int c[N], sa[N], x[N], y[N];void get_sa() {int i, j, k;memset(c, 0, sizeof(c));for (i = 1; i <= n; i ++) {c[x[i] = s[i]] ++;}for (i = 1; i <= m; i ++) {c[i] += c[i - 1];}for (i = n; i >= 1; i --) {sa[c[x[i]]] = i;c[x[i]] --;}for (k = 1; k <= n; k <<= 1) {memset(c, 0, sizeof(c));for (i = 1; i <= n; i ++) {y[i] = sa[i];}for (i = 1; i <= n; i ++) {c[x[y[i] + k]] ++;}for (i = 1; i <= m; i ++) {c[i] += c[i - 1];}for (i = n; i >= 1; i --) {sa[c[x[y[i] + k]]] = y[i];c[x[y[i] + k]] --;}memset(c, 0, sizeof(c));for (i = 1; i <= n; i ++) {y[i] = sa[i];}for (i = 1; i <= n; i ++) {c[x[y[i]]] ++;}for (i = 1; i <= m; i ++) {c[i] += c[i - 1];}for (i = n; i >= 1; i --) {sa[c[x[y[i]]]] = y[i];c[x[y[i]]] --;}for (i = 1; i <= n; i ++) {y[i] = x[i]; }for (m = 0, i = 1; i <= n; i ++) {if (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) {x[sa[i]] = m;}else {m ++;x[sa[i]] = m;}}if (n == m) {break; }}
}int main () {ios::sync_with_stdio(false);cin.tie(0);cin >> s + 1;n = strlen(s + 1);for (int i = n + 1; i <= 2 * n; i ++) {s[i] = s[i - n];}n *= 2;m = 400;get_sa();for (int i = 1; i <= n; i ++) {if ( sa[i] <= (n / 2) ) {cout << s[sa[i] + n / 2 - 1];}}cout << "\n";return 0;
}