zcmu 1550 (문자열의 최소 표현)

제목 링크 : https://acm.zcmu.edu.cn/JudgeOnline/problem.php?id=1550

이야기

문자열 s를 주면 왼쪽으로 회전 할 수 있으며 문자열을 왼쪽으로 여러 번 이동 한 후 사전에 가장 적은 문자열이되도록 요청할 수 있습니다.

예를 들어 s가 (adac)이면 왼쪽으로 순환하면 adac, daca, acad, cada가 될 수 있습니다. 여기서 가장 작은 사전 식 순서는 acad이므로 왼쪽으로 3 번 이동해야합니다.

아이디어

O (n ^ 2) 시간이 초과됩니다. 여기에 O (n) 접근 방식이 있습니다.

i, j는 두 개의 포인터로 두 문자열의 첫 번째 문자의 위치를 ​​나타냅니다. k는 두 문자열의 가장 긴 공통 접두사 길이를 나타냅니다. 두 배열을 사전 순서로 비교해야하므로 i와 j는 같을 수 없으며 i는 0으로 초기화되고 j는 1로 초기화되고 k는 0으로 초기화됩니다.

s [i + k] == s [j + k]이면 공통 접두사 인 k의 길이가 1 씩 증가합니다.

s [i + k]> s [j + k] 일 때 i + k + 1의 위치에 도달하기 위해 k + 1을 점프합니다.

s [i + k] <s [j + k] 일 때, j는 k + 1을 점프하여 j + k + 1의 위치에 도달합니다. 이유는 무엇입니까?

주의해야 할 점은 가장 작은 사전 순서를 가진 문자열로서 s [i]에서 시작하는 문자열보다 작지 않은 s [i + 1에서 j-1]로 시작하는 길이가 같은 문자열이 있어야한다는 것입니다. , 그렇지 않으면 i가 오래 전에 뒤로 건너 뛰고 s [i + k]> s [j + k]이면 j에서 시작하는 아래 첨자가 길이 k의 사전 순서가 더 작음을 나타냅니다.

그런 다음 i를 조정해야합니다 .j에서 시작하는 문자열의 길이가 k이므로 i가 시작된 이후의 k 위치는 실행할 필요가 없습니다. j에서 시작하는 현재 문자열보다 작지 않으므로 i k + 1 개의 위치를 ​​점프해야했고 s [i + k] <s [j + k]이면 마찬가지입니다.

그런 다음 i와 j가 같을 수 없음을 확인하기 위해 우리는 판단 할 필요가 있습니다. 그들이 같으면 j ++로하고 마지막으로 i에서 시작하는 길이 n의 문자열의 사전 순서가 가장 작아야합니다.

ac 코드

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 3e5 + 5;
char s[maxn];
int cal(int n){
    int i = 0, j = 1, k = 0;
    while(i < n && j < n && k < n) {
        int c1 = (i + k) % n, c2 = (j + k) % n;
        if(s[c1] == s[c2]) k ++;
        else {
            if(s[c1] < s[c2]) j += k + 1;
            else i += k + 1;
            if(i == j) j ++;
            k = 0;
        }
    }
    return i;
}
int main(){
    int t; scanf("%d",&t);
    while(t --){
        scanf("%s", s);
        printf("%d\n", cal(strlen(s)));
    }
    return 0;
}

추가 질문

또 다른 유사한 질문  P1368 [템플릿] 최소 표기법

이 방법은 정확히 동일하다고 말할 수 있지만 가장 작은 사전 순서로 문자열을 출력합니다.

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 3e5 + 5;
int s[maxn];
int cal(int n){
    int i = 0, j = 1, k = 0;
    while(i < n && j < n && k < n){
        int d1 = (i + k) % n, d2 = (j + k) % n;
        if(s[d1] == s[d2]) k ++;
        else{
            if(s[d1] > s[d2]) i += k + 1;
            else j += k + 1;
            if(i == j) j ++;
            k = 0;
        }
    }
    return i;
}
int main(){
    int n; scanf("%d",&n);
    for(int i = 0; i < n; i ++) {
        scanf("%d",&s[i]);
    }
    int ans = cal(n);
    for(int i = 0; i < n; i ++) {
        if(i) printf(" ");
        printf("%d", s[(i + ans) % n]);
    }
    puts("");

    return 0;
}

 

추천

출처blog.csdn.net/weixin_43911947/article/details/112674690