zoukankan      html  css  js  c++  java
  • [BZOJ 1692] [Usaco2007 Dec] 队列变换 【后缀数组 + 贪心】

    ---恢复内容开始---

    题目链接:BZOJ - 1692

    题目分析

    首先,有个比较简单的贪心思路:如果当前剩余字符串的两端字母不同,就选取小的字母,这样显然是正确的。

    然而若两端字母相同,我们怎么选取呢?

    这时我们要从两端分别向内部比较,看那一端向内的字符串字典序小。

    比如这个字符串 ABCDBA,从左端向内是 ABC.. 从右端向内是 ABD... 所以就选取左端的字符。

    这样直接比较是 O(n^2) 的,我们可以使用后缀数组的 Rank 数组来比较。

    我们在字符串后加上分隔符,然后再将字符串反转接在后面,求后缀数组的 Rank 数组。

    这样就可以快速比较一个前缀,一个后缀的字典序大小了,具体见代码。

    比如 ABCDBA ,就存成 ABCDBA#ABDCBA 。

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    
    using namespace std;
    
    const int MaxL = 60000 + 15;
    
    int n;
    int A[MaxL], Rank[MaxL], SA[MaxL];
    int VA[MaxL], VB[MaxL], VC[MaxL], Sum[MaxL];
    
    char S[MaxL], Sout[MaxL];
    
    inline bool Cmp(int *a, int x, int y, int l) {
    	return (a[x] == a[y]) && (a[x + l] == a[y + l]);
    }
    
    void DA(int *A, int n, int m) {
    	int *x, *y, *t;
    	x = VA; y = VB;
    	for (int i = 1; i <= m; ++i) Sum[i] = 0;
    	for (int i = 1; i <= n; ++i) ++Sum[x[i] = A[i]];
    	for (int i = 2; i <= m; ++i) Sum[i] += Sum[i - 1];
    	for (int i = n; i >= 1; --i) SA[Sum[x[i]]--] = i;
    	int p, q;
    	p = 0;
    	for (int j = 1; p < n; j <<= 1, m = p) {
    		q = 0;
    		for (int i = n - j + 1; i <= n; ++i) y[++q] = i;
    		for (int i = 1; i <= n; ++i) {
    			if (SA[i] <= j) continue;
    			y[++q] = SA[i] - j;
    		}
    		for (int i = 1; i <= n; ++i) VC[i] = x[y[i]];
    		for (int i = 1; i <= m; ++i) Sum[i] = 0;
    		for (int i = 1; i <= n; ++i) ++Sum[VC[i]];
    		for (int i = 2; i <= m; ++i) Sum[i] += Sum[i - 1];
    		for (int i = n; i >= 1; --i) SA[Sum[VC[i]]--] = y[i];
    		t = x; x = y; y = t;
    		x[SA[1]] = 1; p = 1;
    		for (int i = 2; i <= n; ++i) 
    			x[SA[i]] = Cmp(y, SA[i], SA[i - 1], j) ? p : ++p;
    	}
    	for (int i = 1; i <= n; ++i) Rank[SA[i]] = i;
    }
    
    int main() 
    {
    	scanf("%d", &n);
    	for (int i = 1; i <= n; ++i) {
    		cin >> S[i];
    		A[i] = S[i] - 'A' + 1;
    		A[2 * n - i + 2] = A[i];
    	}
    	A[n + 1] = 27;
    	A[n * 2 + 2] = 28;
    	DA(A, n * 2 + 2, 28);
    	int l = 1, r = n, Top = 0;
    	while (l <= r) {
    		if (S[l] != S[r]) {
    			if (S[l] < S[r]) { 
    				Sout[++Top] = S[l];
    				++l;
    			} 
    			else {
    				Sout[++Top] = S[r];
    				--r;
    			}
    			continue;
    		}
    		if (l == r) {
    			Sout[++Top] = S[l];
    			break;
    		}
    		if (Rank[l] < Rank[n * 2 - r + 2]) {
    			Sout[++Top] = S[l];
    			++l;
    		}
    		else {
    			Sout[++Top] = S[r];
    			--r;
    		}
    	}
    	for (int i = 1; i <= Top; ++i) {
    		printf("%c", Sout[i]);
    		if (i % 80 == 0) printf("
    ");
    	}
    	return 0;
    }
    

      

    ---恢复内容结束---

  • 相关阅读:
    TSQL编程的全局变量
    一、读大学,究竟读什么?
    受用一生的心理寓言
    字符串函数
    android wait notify实现线程挂起与恢复
    Java Thread.interrupt 中断JAVA线程
    android实现文件下载功能的3种方法
    Android startActivityForResult 和 setResult的使用
    Android 软键盘盖住输入框的问题
    Android蓝牙操作
  • 原文地址:https://www.cnblogs.com/JoeFan/p/4214919.html
Copyright © 2011-2022 走看看