【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;
}
posted @ 2021-10-20 19:56  Jayun  阅读(29)  评论(0编辑  收藏  举报