【bzoj 1692】后缀数组

将一个字符串进行如下操作:每次拿去首或尾,放在新字符串尾,问新字符串字典序最小的情况。

直觉贪心,但情况复杂。可以用后缀数组,每次比较当前剩余串正序和倒序哪种字典序小,可以如下构建:

ABCD -> ABCD0DCBA

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#define Rep(i, x, y) for (int i = x; i <= y; i ++)
#define RepE(i, x) for (int i = pos[x]; i; i = g[i].nex)
#define Dwn(i, x, y) for (int i = x; i >= y; i --)
using namespace std;
typedef long long LL;
const int N = 60005;
int n, a[N], t1[N], t2[N], c[N], n0, sa[N], rk[N], az;
char s[N], ans[N];
bool Cmp(int *r, int x, int y, int l) { return r[x] == r[y] && r[x+l] == r[y+l]; }
void Bsa(int *a, int n, int m) {
	int *x = t1, *y = t2, p = 0;
	Rep(i, 0, m) c[i] = 0;
	Rep(i, 0, n) c[x[i] = a[i]] ++;
	Rep(i, 1, m) c[i] += c[i-1];
	Rep(i, 0, n) sa[-- c[ x[i] ]] = i;
	for (int j = 1; p <= n; j <<= 1, m = p) {
		p = 0;
		Rep(i, n-j+1, n) y[p ++] = i;
		Rep(i, 0, n) if (sa[i] >= j) y[p ++] = sa[i] - j;
		Rep(i, 0, m) c[i] = 0;
		Rep(i, 0, n) c[ x[y[i]] ] ++;
		Rep(i, 1, m) c[i] += c[i-1];
		Dwn(i, n, 0) sa[-- c[ x[y[i]] ]] = y[i];
		swap(x, y), p = 1, x[ sa[0] ] = 0;
		Rep(i, 1, n) x[ sa[i] ] = Cmp(y, sa[i], sa[i-1], j) ? p - 1 : p ++;
	}
	Rep(i, 0, n) rk[ sa[i] ] = i;
}
int main()
{
	scanf ("%d", &n);
	Rep(i, 0, n-1) scanf (" %c", &s[i]), a[i] = a[n*2-i] = s[i];
	n0 = n, n = n * 2 + 1;
	Bsa(a, n, 256), n --;
	for (int i = 0, j = n0-1; i <= j; ) {
		if (rk[i] < rk[n-j]) ans[++ az] = s[i], i ++;
		else ans[++ az] = s[j], j --;
	}
	for (int i = 1; i <= n0; i ++) {
		printf ("%c", ans[i]);
		if (i % 80 == 0) puts("");
	}
	if (az % 80 != 0) puts("");

    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值