usaco5.5.2 Hidden Password

博客介绍了如何从字符串的字典序排列中找到隐藏密码的起始位置,即找到排序后的第一个字符串的第一个字符在原字符串中的位置。题目来源于ACM South Eastern Europe 2003竞赛,给出字符串S,需要找出所有左循环移位排序后的前缀,并返回最小的起始位置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一 原题

Hidden Password

ACM South Eastern Europe -- 2003

Sometimes the programmers have very strange ways of hiding their passwords. Billy "Hacker" Geits chooses a string S composed of L (5 <= L <= 100,000) lowercase letters ('a'..'z') with length L. Then he makes and sorts all L-1 one-letter left cyclic shifts of the string. He then takes as a password one prefix of the lexicographically first of the obtained strings (including S).

For example consider the string "alabala". The sorted cyclic one-letter left shifts (including the initial string) are:


aalabal 
abalaal 
alaalab 
alabala 
balaala 
laalaba 
labalaa

Lexicographically, first string is 'aalabal'. The first letter of this string ('a') is the 'a' that was in position 6 in the initial string (counting the first letter in the string as position 0).

Write a program that, for given string S, finds the start position of the first letter of the sorted list of cyclic shifts of the string. If the first element appears more than once in the sorted list, then the program should output the smallest possible initial position.

PROGRAM NAME: hidden

INPUT FORMAT

  • Line 1: A single integer: L
  • Line 2..?: All L characters of the string S, broken across lines such that each line has 72 characters except the last one, which might have fewer.

SAMPLE INPUT (file hidden.in)

7
alabala

OUTPUT FORMAT

  • Line 1: A single integer that is the start position of the first letter, as described above.

SAMPLE OUTPUT (file hidden.out)

6

二 分析

裸的字符串最小表示法,时间复杂度O(n)。


三 代码

运行结果:
USER: Qi Shen [maxkibb3]
TASK: hidden
LANG: C++

Compiling...
Compile: OK

Executing...
   Test 1: TEST OK [0.000 secs, 4244 KB]
   Test 2: TEST OK [0.000 secs, 4252 KB]
   Test 3: TEST OK [0.000 secs, 4248 KB]
   Test 4: TEST OK [0.000 secs, 4248 KB]
   Test 5: TEST OK [0.000 secs, 4244 KB]
   Test 6: TEST OK [0.000 secs, 4248 KB]
   Test 7: TEST OK [0.000 secs, 4252 KB]
   Test 8: TEST OK [0.000 secs, 4248 KB]
   Test 9: TEST OK [0.000 secs, 4244 KB]
   Test 10: TEST OK [0.000 secs, 4244 KB]
   Test 11: TEST OK [0.000 secs, 4248 KB]
   Test 12: TEST OK [0.000 secs, 4248 KB]
   Test 13: TEST OK [0.000 secs, 4248 KB]
   Test 14: TEST OK [0.000 secs, 4244 KB]

All tests OK.

Your program ('hidden') produced all correct answers! This is your submission #19 for this problem. Congratulations!


AC代码:
/*
ID:maxkibb3
LANG:C++
PROB:hidden
*/

#include<cstdio>
#include<cstring>
#include<algorithm>

const int MAX_LETTER_NUM = 2e5 + 10;
const int LETTER_PER_LINE = 72;

int min_representation(char *_s, int _len) {
    memcpy(_s + _len, _s, _len);
    int i = 0, j = 1;
    while(i < _len && j < _len) {
        int k = 0;
        while(_s[i + k] == _s[j + k]) {
            k++;
            if(k == _len) return std::min(i, j);
        }
        if(_s[i + k] > _s[j + k]) i += k + 1;
        if(_s[i + k] < _s[j + k]) j += k + 1;
        if(i == j) j++;
    }
    if(j >= _len) return i;
    else return j;
}

void solve() {
    int len;
    char s[MAX_LETTER_NUM] = {'\0'};
    freopen("hidden.in", "r", stdin);
    scanf("%d", &len);
    char *ptr = s;
    while(scanf("%s", ptr) != -1) {
        ptr = ptr + 72;
    }
    memcpy(s + len, s, len);
    freopen("hidden.out", "w", stdout);
    printf("%d\n", min_representation(s, len));
}

int main() {
    solve();
    return 0;
}

运行结果2:
USER: Qi Shen [maxkibb3]
TASK: hidden
LANG: C++

Compiling...
Compile: OK

Executing...
   Test 1: TEST OK [0.000 secs, 10544 KB]
   Test 2: TEST OK [0.000 secs, 10544 KB]
   Test 3: TEST OK [0.000 secs, 10544 KB]
   Test 4: TEST OK [0.000 secs, 10544 KB]
   Test 5: TEST OK [0.000 secs, 10544 KB]
   Test 6: TEST OK [0.000 secs, 10544 KB]
   Test 7: TEST OK [0.028 secs, 10544 KB]
   Test 8: TEST OK [0.476 secs, 10544 KB]
   Test 9: TEST OK [0.504 secs, 10544 KB]
   Test 10: TEST OK [0.490 secs, 10544 KB]
   Test 11: TEST OK [0.294 secs, 10544 KB]
   Test 12: TEST OK [0.042 secs, 10704 KB]
   Test 13: TEST OK [0.000 secs, 10544 KB]
   Test 14: TEST OK [0.112 secs, 10860 KB]

All tests OK.

Your program ('hidden') produced all correct answers! This is your submission #28 for this problem. Congratulations!


AC代码2(后缀数组):
/*
TASK:hidden
ID:maxkibb3
LANG:C++
*/


#include<cstdio>
#include<cstring>
#include<algorithm>


const int MAX = 2e5 + 10;
const int LETTER_PER_LINE = 72;


char Str[MAX];
int Len, Step;
int Bucket[MAX], BucketCopy[MAX];


struct Suffix {
    int idx;


    bool operator < (const Suffix &_o) const {
        if(Step == 0) return Str[idx] < Str[_o.idx];
        if(Bucket[idx] == Bucket[_o.idx])
            return Bucket[idx + Step] < Bucket[_o.idx + Step];
        else return Bucket[idx] < Bucket[_o.idx];
    }


    bool operator == (const Suffix &_o) const {
        return !(*this < _o || _o < *this);
    }
} Suf[MAX];


bool update_bucket() {
    bool ret = true;
    int cnt = 1;
    for(int i = 0; i < Len; i++) {
        if(i != 0 && !(Suf[i] == Suf[i - 1])) cnt++;
        if(cnt == Len) ret = false;
        BucketCopy[Suf[i].idx] = cnt;
    }
    memcpy(Bucket, BucketCopy, sizeof(BucketCopy));
    return ret;
}


void suffix_sort(char *_s) {
    for(int i = 0; i < Len; i++) Suf[i].idx = i;
    // 当字符串相等时,题目要求输出最小下标,必须使用stable_sort
    std::stable_sort(Suf, Suf + Len);
    bool flag = update_bucket();
    // Step的上界是输入字符串长度的一半
    for(Step = 1; flag && Step <= Len / 4; Step *= 2) {
        std::stable_sort(Suf, Suf + Len);
        flag = update_bucket();
    }
}


void init() {
    freopen("hidden.in", "r", stdin);
    freopen("hidden.out", "w", stdout);
    scanf("%d", &Len);
    for(int i = 0; ; i++) {
        int tmp = scanf("%s", Str + LETTER_PER_LINE * i);
        if(tmp == -1) break;
    }
    memcpy(Str + Len, Str, sizeof(char) * Len);
    Len *= 2;
    Str[Len] = '\0';
}


int main() {
    init();
    suffix_sort(Str);
    for(int i = 0; ; i++)
        if(Suf[i].idx < Len / 2) { printf("%d\n", Suf[i].idx); break; }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值