题目描述

Farmer John 打算带领 NN(1 \leq N \leq 5 \times 10^51≤N≤5×10
5
)头奶牛参加一年一度的”全美农场主大奖赛“。在这场比赛中,每个参赛者必须让他的奶牛排成一列,然后带领这些奶牛从裁判面前依此走过。

今年,竞赛委员会在接受报名时,采用了一种新的登记规则:取每头奶牛名字的首字母,按照它们在队伍中的次序排成一列。将所有队伍的名字按字典序升序排序,从而得到出场顺序。

FJ 由于事务繁忙,他希望能够尽早出场。因此他决定重排队列。

他的调整方式是这样的:每次,他从原队列的首端或尾端牵出一头奶牛,将她安排到新队列尾部。重复这一操作直到所有奶牛都插入新队列为止。

现在请你帮 FJ 算出按照上面这种方法能排出的字典序最小的队列。

输入格式
第一行一个整数 NN。

接下来 NN 行每行一个大写字母,表示初始队列。

输出格式
输出一个长度为 NN 的字符串,表示可能的最小字典序队列。

每输出 8080 个字母需要一个换行。

输入输出样例
输入 #1复制

6
A
C
D
B
C
B

输出 #1复制

ABCBCD

题解:

样例分析:
ACDBCB
先出A,此时队列[A]
再出B,队列[AB]
再出C,队列[ABC]
再出B,队列[ABCB]
再出C,队列[ABCBC]
再出D,队列[ABCBCD]
此时不难看出贪心做,就直接比较原队列两端字母的大小,如果两端一样就再往里一位,直到出现大小关系。但是存在这样一个问题,如果原队列很长,前两边相等的很多,这样极易爆时,例如AA...ABCA....AA
我们可以用f[i]表示i开始的后缀,g[i]表示i开始的前缀(即把以i结尾的前缀倒过来),这样当str[L] == str[R]时,我们就可以直接比较f[L]与g[R]
数组f与g如何计算?暴力肯定不可取
我们可以将原队列翻转,就比如ACDBCB--->ACDBCB#BCBDCA,两者之间加一个其他字符(例如#,其实为什么加这个字符我也不是很清楚,貌似不加也可以过???)
因为我们将字符串倒过来,所以g是以i为开头的前缀其实就是后缀(数组g在翻转字符部分),懂没懂?也就是现在字符串分为前半部分为正序,后半部分为倒序,我们对正序取后缀,对倒序取前缀,其实本质都是取后缀,所以我们对新串进行后缀数组,
f[i]就是rank[i],g[i]就是rank[2(n+1)-i]
(Rank[i]=后缀suf(i)的排名, suf(k)为s(k…n)构成的子串)
比如acabca,处理完就是acabca#acbaca
比较两个c的时候,其实就是比较第一个c的排名和倒数第二个c的排名

代码:

在这里插入图片描述

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

char str[N];
string ans;
int n, m, sa[N], rk[N], oldrk[N<<1], pos[N], cnt[N],id[N],px[N];
bool cmp(int x, int y, int k)
{
    return oldrk[x] == oldrk[y] && oldrk[x + k] == oldrk[y + k];
}
void getsa()
{
    m = 122;int i,w,p;
    for (i = 1; i <= n; ++i) ++cnt[rk[i] = str[i]];
  for (i = 1; i <= m; ++i) cnt[i] += cnt[i - 1];
  for (i = n; i >= 1; --i) sa[cnt[rk[i]]--] = i;

  for (w = 1; w < n; w <<= 1, m = p) {  // m=p 就是优化计数排序值域
    for (p = 0, i = n; i > n - w; --i) id[++p] = i;
    for (i = 1; i <= n; ++i)
      if (sa[i] > w) id[++p] = sa[i] - w;
    memset(cnt, 0, sizeof(cnt));
    for (i = 1; i <= n; ++i) ++cnt[px[i] = rk[id[i]]];
    for (i = 1; i <= m; ++i) cnt[i] += cnt[i - 1];
    for (i = n; i >= 1; --i) sa[cnt[px[i]]--] = id[i];
    memcpy(oldrk, rk, sizeof(rk));
    for (p = 0, i = 1; i <= n; ++i)
      rk[sa[i]] = cmp(sa[i], sa[i - 1], w) ? p : ++p;
  }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        getchar();
        scanf("%c",&str[i]);
    }
    str[n+1]='0';
    for(int i=n+2;i<=n*2+1;i++)
        str[i]=str[2*n-i+2];
    n=n*2+1;
    getsa();
    int l=1,r=n/2;
    while(l<=r)
    {
        if(str[l]<str[r]) ans+=str[l++];
        else if(str[l]>str[r]) ans+=str[r--];
        else
        {
            if(rk[l]<rk[n/2+n/2-r+2]) ans+=str[l++];
            else ans+=str[r--];
        }
    }
    for(int i=0;i<ans.size();i++)
    {
        printf("%c",ans[i]);
        if((i+1)%80==0) printf("\n");
    }
    return 0;
}

在这里插入图片描述

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

char str[N];
string ans;
int n, m, sa[N], rk[N], oldrk[N<<1], pos[N], cnt[N];
bool cmp(int x, int y, int k)
{
    return oldrk[x] == oldrk[y] && oldrk[x + k] == oldrk[y + k];
}
void getsa()
{
    m = 122;
    for (int i = 1; i <= n; i++)
        ++cnt[rk[i] = str[i]];
    for (int i = 1; i <= m; i++)
        cnt[i] += cnt[i - 1];
    for (int i = n; i; i--)
        sa[cnt[rk[i]]--] = i;
    for (int k = 1; k <= n; k <<= 1)
    {
        int num = 0;
        for (int i = n - k + 1; i <= n; i++)
            pos[++num] = i;
        for (int i = 1; i <= n; i++)
        {
            if (sa[i] > k)
                pos[++num] = sa[i] - k;
        }
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=n;i++)
            ++cnt[rk[i]];
        for(int i=1;i<=m;i++)
            cnt[i]+=cnt[i-1];
        for (int i = n; i; i--)
            sa[cnt[rk[pos[i]]]--] = pos[i];
        num = 0;
        memcpy(oldrk, rk, sizeof(rk));
        for (int i = 1; i <= n; i++)
            rk[sa[i]]=cmp(sa[i],sa[i-1],k)?num:++num;
        if(num==n) break;
        m=num;
    }
    for(int i=1;i<=n;i++)
        rk[sa[i]]=i;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        getchar();
        scanf("%c",&str[i]);
    }
    str[n+1]='0';
    for(int i=n+2;i<=n*2+1;i++)
        str[i]=str[2*n-i+2];
    n=n*2+1;
    getsa();
    int l=1,r=n/2;
    while(l<=r)
    {
        if(str[l]<str[r]) ans+=str[l++];
        else if(str[l]>str[r]) ans+=str[r--];
        else
        {
            if(rk[l]<rk[n/2+n/2-r+2]) ans+=str[l++];
            else ans+=str[r--];
        }
    }
    for(int i=0;i<ans.size();i++)
    {
        printf("%c",ans[i]);
        if((i+1)%80==0) printf("\n");
    }
    return 0;
}