【Luogu P2870】[USACO07DEC]Best Cow Line G
链接:
题目大意:
给定一个串,每次选择最前或最后的字符,求最后能得到字典序最小的串。
正文:
贪心时遇到 \(\texttt{BACB}\) 这样的串时,不能随便选,于是想到了后缀数组。那么取最后的字符,就相当于把原串反过来。所以先对原串如此处理:\(S'=S+\texttt{@}+\bar{S}\),其中 \(\texttt{@}\) 表示特殊符号,\(\bar{S}\) 表示 \(S\) 翻转后的串。
代码:
const int N = 2e6 + 10;
inline ll Read()
{
ll x = 0, f = 1;
char c = getchar();
while (c != '-' && (c < '0' || c > '9')) c = getchar();
if (c == '-') f = -f, c = getchar();
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
return x * f;
}
int n, m = 122;
int sa[N], x[N], y[N], c[N];
char s[N];
void SA()
{
for (int i = 1; i <= m; i++) c[i] = 0;
for (int i = 1; i <= n; i++) c[x[i] = s[i]]++;
for (int i = 2; i <= m; i++) c[i] += c[i - 1];
for (int i = n; i; i--) sa[c[x[i]]--] = i;
for (int k = 1; k <= n; k <<= 1)
{
int num = 0;
for (int i = n - k + 1; i <= n; i ++) y[++num] = i;
for (int i = 1; i <= n; i++)
if (sa[i] > k) y[++num] = sa[i] - k;
for (int i = 1; i <= m; i++) c[i] = 0;
for (int i = 1; i <= n; i++) c[x[i]]++;
for (int i = 2; i <= m; i++) c[i] += c[i - 1];
for (int i = n; i; i--) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
swap (x, y);
x[sa[1]] = 1, num = 1;
for (int i = 2; i <= n; i++)
x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k])? num: ++num;
m = num;
if (n == m) break;
}
return ;
}
int main()
{
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
int k = Read();
for (int i = 1; i <= k; i++)
{
char c = getchar();
while (c < 'A' || c > 'Z') c = getchar();
s[i] = c;
}
n = k << 1 | 1;
for (int i = 1; i <= k; i++) s[k + 1 + i] = s[k - i + 1];
s[k + 1] = 'Z' + 1;
SA();
for (int i = 1, j = k, tot = 1; i <= j; tot++)
{
if (x[i] < x[n - j + 1]) putchar(s[i++]);
else putchar(s[j--]);
if (!(tot % 80)) putchar(10);
}
return 0;
}