poj 3623 Best Cow Line, Gold 后缀数组 + 贪心

题目链接

题目描述

对于一个给定的字符串,可以从左右两端取字符,依次排列构成一个新的字符串。

求可能构成的字符串中字典序 最小的一个。

例:ACDBCB -> ABCBCD

思路

参考自 xueyifan1993.

正确的 贪心 姿势:

记左端位置为 \(l\),右端位置为 \(r\),比较 \(suffix(l)\) 与 \(inv(prefix(r))\),取小者。

比较 则显然是借助 \(rank\) 数组。

// 以及正确的I/O姿势真的重要...
// scanf+printf:1172ms -> getchar+putchar: 219ms

Code

#include <stdio.h>
#include <iostream>
#define maxn 60010
using namespace std;
typedef long long LL;
int a[maxn], wa[maxn], wb[maxn], wv[maxn], wt[maxn], h[maxn], rk[maxn], sa[maxn], n, r[maxn];
char s[maxn], ans[maxn];
bool cmp(int* r, int a, int b, int l) { return r[a] == r[b] && r[a+l] == r[b+l]; }
void init(int* r, int* sa, int n, int m) {
    int* x=wa, *y=wb, *t, i, j, p;
    for (i = 0; i < m; ++i) wt[i] = 0;
    for (i = 0; i < n; ++i) ++wt[x[i] = r[i]];
    for (i = 1; i < m; ++i) wt[i] += wt[i - 1];
    for (i = n-1; i >= 0; --i) sa[--wt[x[i]]] = i;

    for (j = 1, p = 1; p < n; j <<= 1, m = p) {
        for (p = 0, i = n-j; i < n; ++i) y[p++] = i;
        for (i = 0; i < n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j;

        for (i = 0; i < n; ++i) wv[i] = x[y[i]];

        for (i = 0; i < m; ++i) wt[i] = 0;
        for (i = 0; i < n; ++i) ++wt[wv[i]];
        for (i = 1; i < m; ++i) wt[i] += wt[i - 1];
        for (i = n-1; i >= 0; --i) sa[--wt[wv[i]]] = y[i];

        t = x, x = y, y = t, x[sa[0]] = 0;
        for (p = 1, i = 1; i < n; ++i) x[sa[i]] = cmp(y, sa[i], sa[i-1], j) ? p - 1 : p++;
    }

    for (i = 0; i < n; ++i) rk[sa[i]] = i;
}
inline void read(char &x) {
    char ch;
    while (ch=getchar(),ch>'Z' || ch<'A') ;
    x=ch;
}
int main() {
    scanf("%d", &n);
    int m = 0;
    for (int i = 0; i < n; ++i) {
        read(s[i]);
        r[2*n-1-i] = r[i] = s[i];
        m = max(r[i], m);
    }
    int tot = n << 1;
    r[tot++] = 0;
    init(r, sa, tot, ++m);
    int l = 0, r = n-1, cnt=0;
    while (l < r) {
        if (rk[l] > rk[2*n-1-r]) ans[cnt++] = s[r--];
        else ans[cnt++] = s[l++];
    }
    ans[cnt++] = s[l];
    for (int i = 0; i < cnt; ++i) {
        if (i && i % 80==0) puts("");
        putchar(ans[i]);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/kkkkahlua/p/8367548.html

时间: 2024-08-02 10:43:56

poj 3623 Best Cow Line, Gold 后缀数组 + 贪心的相关文章

POJ 3623 Best Cow Line, Gold(模拟)

题意  给你一个字符序列   你每次可以从它的头部或尾部拿出一个字符组成一个新的字符序列   输出这样做能达到的最小的字符序列   每行最多输出80个字符(开始被这个坑了好久) 直接模拟就行  哪边小就选哪边  相等就往内看 #include<cstdio> #include<iostream> #include<string> using namespace std; const int N = 30010; int main() { char s[N][2]; in

POJ 3623 Best Cow Line, Gold(字符串处理)

题意:给你一个字符串,让你重新排列,只能从头或者尾部取出一个放到新字符串队列的最后.按照字典序. 解决方法:比较前后两个的大小,谁小输出谁,相等,就往当中比来确定当前应该拿最前面的还是最后面的,如果再相等就继续.... 所以比较这个动作的单一功能,可以写成一个check函数,方便操作也方便递归. #include<iostream> #include<cstring> using namespace std; #define MAX 30005 char str[MAX]; int

[贪心+后缀数组] poj 3623 Best Cow Line, Gold

题意: 给N个字符,每次只能取第一个或者最后一个,问构成最小字典序的串是什么. 思路: 贪心,每次取字典序最小的串,的首字符. 其实就是l和r比较一下构成的串那个字典序小. 这里运用后缀数组要实现O(1)的查询. 将原串反拼进串内做da. 然后根据ra判断. 代码: #include"cstdlib" #include"cstdio" #include"cstring" #include"cmath" #include&quo

POJ3623:Best Cow Line, Gold(后缀数组)

Description FJ is about to take his N (1 ≤ N ≤ 30,000) cows to the annual"Farmer of the Year" competition. In this contest every farmer arranges his cows in a line and herds them past the judges. The contest organizers adopted a new registration

HDU 3623 Best Cow Line, Gold(模拟,注意思路,简单)

题目 POJ 3617 和 这道题题目一样,只是范围稍稍再小一点. //模拟试试 #include<stdio.h> #include<string.h> #include<algorithm> using namespace std; char s[30010][2]; bool bijiao(int st,int ed) { if(st==ed) return true; if(s[st][0]<s[ed][0]) return true; else if(s

POJ 题目3623 Best Cow Line, Gold(后缀数组rank简单应用)

Best Cow Line, Gold Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 5063   Accepted: 1806 Description FJ is about to take his N (1 ≤ N ≤ 30,000) cows to the annual"Farmer of the Year" competition. In this contest every farmer arrang

P2870 [USACO07DEC]最佳牛线,黄金Best Cow Line, Gold

题目描述 FJ is about to take his N (1 ≤ N ≤ 30,000) cows to the annual"Farmer of the Year" competition. In this contest every farmer arranges his cows in a line and herds them past the judges. The contest organizers adopted a new registration scheme

P2870 [USACO07DEC]最佳牛线,黄金Best Cow Line, Gold 解题报告

P2870 [USACO07DEC]最佳牛线,黄金Best Cow Line, Gold 题意 给一个字符串,每次可以从两边中的一边取一个字符,要求取出的字符串字典序最小 可以Hash+二分 也可以SA 首先贪心选字典序小的 然后遇到相等的了比Rank数组,把原串倍长一下就可以比了. Code: #include <cstdio> #include <algorithm> const int N=6e4+10; char s[N],ans[N]; int sa[N],Rank[N]

POJ 3693 Maximum repetition substring (后缀数组)

题目大意: 求出字典序最小,重复次数最多,的子串. 思路分析: RMQ + height 数组可以求出任意两个后缀的lcp 我们枚举答案字符串的重复的长度. 如果这个字符串的长度为 l ,而且这个字符串出现过两次或两次以上 那么你会发现在原串中  str[0] str[l] str[2*l] ....肯定有相邻的两个被包含在重复的串中. 我们求出这两个相邻的后缀的lcp 我们上面仅仅说的是被包含在重复的串中,但并不一定就是以 str[0], str[l],str[2*l]....为起点的. 那我