hdu5442 (2015 ACM/ICPC Asia Regional Changchun Online)

题面

Lulu has a sweet tooth. Her favorite food is ring donut. Everyday she buys a ring donut from the same bakery. A ring donut is consists of n parts. Every part has its own sugariness that can be expressed by a letter from a to z (from low to high), and a ring donut can be expressed by a string whose i-th character represents the sugariness of the i−th part in clockwise order. Note that z is the sweetest, and two parts are equally sweet if they have the same sugariness.

Once Lulu eats a part of the donut, she must continue to eat its uneaten adjacent part until all parts are eaten. Therefore, she has to eat either clockwise or counter-clockwise after her first bite, and there are 2n ways to eat the ring donut of n parts. For example, Lulu has 6 ways to eat a ring donut abc: abc,bca,cab,acb,bac,cba. Lulu likes eating the sweetest part first, so she actually prefer the way of the greatest lexicographic order. If there are two or more lexicographic maxima, then she will prefer the way whose starting part has the minimum index in clockwise order. If two ways start at the same part, then she will prefer eating the donut in clockwise order. Please compute the way to eat the donut she likes most.

题意

给你一个字符串首尾相接,让你找一个起始位置i,从i走n步(要么顺时针走要么逆时针走)最后让这个串字典序最大(字典序最大>起始位置最小>顺时针优先)

思路

把字符串*2, 然后通过后缀数组处理出来sa数组,找到字典序最大且长度>=n的后缀

把字符串反过来,进行相同的操作,最后比较这两次找到的答案即可。

注意:字符串反序之后下标要重新对应

代码

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
const int MAXN=40040;
int t1[MAXN],t2[MAXN],c[MAXN];//求SA数组需要的中间变量,不需要赋值
//待排序的字符串放在s数组中,从s[0]到s[n-1],长度为n,且最大值小于m,
//除s[n-1]外的所有s[i]都大于0,r[n-1]=0
//函数结束以后结果放在sa数组中
//sa[1~n]->[0,N) rank[0~n-1]->[1,N]  height[1~n]
bool cmp(int *r,int a,int b,int l)
{
    return r[a] == r[b] && r[a+l] == r[b+l];
}
void da(int str[],int sa[],int Rank[],int height[],int n,int m)
{
    n++;
    int i, j, p, *x = t1, *y = t2;
    //第一轮基数排序,如果s的最大值很大,可改为快速排序
    for(i = 0;i < m;i++)c[i] = 0;
    for(i = 0;i < n;i++)c[x[i] = str[i]]++;
    for(i = 1;i < m;i++)c[i] += c[i-1];
    for(i = n-1;i >= 0;i--)sa[--c[x[i]]] = i;
    for(j = 1;j <= n; j <<= 1)
    {
        p = 0;
        //直接利用sa数组排序第二关键字
        for(i = n-j; i < n; i++)y[p++] = i;//后面的j个数第二关键字为空的最小
        for(i = 0; i < n; i++)if(sa[i] >= j)y[p++] = sa[i] - j;
        //这样数组y保存的就是按照第二关键字排序的结果
        //基数排序第一关键字
        for(i = 0; i < m; i++)c[i] = 0;
        for(i = 0; i < n; i++)c[x[y[i]]]++;
        for(i = 1; i < m;i++)c[i] += c[i-1];
        for(i = n-1; i >= 0;i--)sa[--c[x[y[i]]]] = y[i];
        //根据sa和x数组计算新的x数组
        swap(x,y);
        p = 1; x[sa[0]] = 0;
        for(i = 1;i < n;i++)
            x[sa[i]] = cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        if(p >= n)break;
        m = p;//下次基数排序的最大值
    }
    int k = 0;
    n--;
    for(i = 0;i <= n;i++)Rank[sa[i]] = i;
    for(i = 0;i < n;i++)
    {
        if(k)k--;
        j = sa[Rank[i]-1];
        while(str[i+k] == str[j+k])k++;
        height[Rank[i]] = k;
    }
}
int Rank[MAXN],height[MAXN];

char str[MAXN];
int r[MAXN];
int sa[MAXN];
int A[MAXN];
char r1[MAXN], r2[MAXN];

int f(int pos, int nn) {
    if(pos >= nn) return pos - nn;
    else return pos;
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--) {
        int n;
        scanf("%d",&n);
        int nn = n;
        scanf("%s",str);
        for(int i = 0; i < n; i++) r[i] = r[i+n]= str[i];
        n <<= 1;
        r[n] = 0;
        da(r,sa,Rank,height,n,128);
        int mi = f(sa[n], nn);

        int now = n;
        while(height[now] >= nn && now >= 2) {
            now--;
            if(f(sa[now], nn) < mi) {
                mi = f(sa[now], nn);
            }
        }
        for(int i = 0; i < nn; i++) r1[i] = r[mi+i];
        r1[nn] = '\0';

        for(int i = 0; i < nn; i++) r[i] = r[i+nn] = str[nn-i-1];
        r[n] = 0;

        da(r,sa,Rank,height,n,128);
        int mx = f(sa[n], nn);
        now = n;
        while(height[now] >= nn && now >= 2) {

            now--;
            if(f(sa[now], nn) > mx) {
                mx = f(sa[now], nn);
            }
        }
        for(int i = 0; i < nn; i++) r2[i] = r[mx+i];
        r2[nn] = '\0';
        int cmp = strcmp(r1,r2);
        if(cmp < 0) {
            printf("%d %d\n",nn-mx,1);
        }
        else if(!cmp && nn-mx+1<mi) {
            printf("%d %d\n",nn-mx,1);
        }
        else printf("%d %d\n",mi+1,0);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值