Title link: https://acm.zcmu.edu.cn/JudgeOnline/problem.php?id=1550
Topic
Give you a string s, which can be moved to the left circularly, and the string lexicographical order is the smallest after at least a few left shifts.
Cycle left, for example s is (adac) then left shift can be adac, daca, acad, cada. The smallest lexicographic order here is acad, so it needs to be shifted left 3 times.
Ideas
O(n^2) will time out, here is an O(n) approach
i, j are two pointers, indicating the position of the first letter of the two strings. k represents the longest common prefix length of two strings. Since the two arrays need to be compared in lexicographic order, i and j cannot be the same, and i is initialized to 0, j is initialized to 1, and k is initialized to 0
When s[i+k] == s[j+k], the length of k as a common prefix is increased by 1.
When s[i+k]> s[j+k], i will jump k+1 to reach the position of i+k+1
When s[i+k] <s[j+k], j will jump k+1 to reach the position of j+k+1, why?
What we should pay attention to is that, as a string with the smallest lexicographical order, there must be strings with the same length starting from s[i+1 to j-1] that are not less than those starting from s[i], otherwise i jumped to the back a long time ago, then if s[i+k]>s[j+k], then it indicates that the subscript starting from j has a smaller lexicographical order of length k.
Then i needs to be adjusted. Since the length of the string starting from j is k, then the k positions after i start do not need to run, because they will not be smaller than the current string starting from j, so i had to jump k+1 Positions. If s[i+k]<s[j+k], the same is true.
Then in order to ensure that i and j cannot be the same, we need to judge.If they are the same, we let j++, and finally the lexicographic order of the string of length n starting from i must be the smallest.
ac code
#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;
}
Additional questions
Another similar question P1368 [Template] Minimal Notation
The method can be said to be exactly the same, but it outputs the string with the smallest lexicographic order
#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;
}