【题解】P1368【【模板】最小表示法】
最小表示法
给定一个字符串(或数组),可以将字符串末尾字母移动到最前面,也可以将首位字母移动到最前面,问移动若干次后,字典序最小的字符串?
我们可以使用双指针的做法,下面讲解一下双指针做法的过程与正确性:
-
seq[i]不等于seq[j]不妨设
seq[i] < seq[j]由于
seq[j]太大了,因此我们保留seq[i],将j加1 -
seq[i]等于seq[j]假设从
seq[i]与seq[j]开始,连续k个数都相等,第k+1个数不相等,那么:不妨设
seq[(i + k) % n] < seq[(j + k) % n]因此以
j, (j + 1) % n, ..., (j + k) % n开头的"答案"均可被i, (i + 1) % n, ..., (i + k) % n省略掉,可以将j加上k + 1
仔细观察发现第一种分类可以归为第二种分类k=0时的情况
因此,理论上来讲,这其实是一个三指针做法好吧我瞎编的
代码:
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 300001;
int seq[MAXN];
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &seq[i]);
}
int i, j, k;
for (i = 0, j = 1, k = 0; i < n && j < n && k < n; ) {
if (seq[(i + k) % n] == seq[(j + k) % n]) {
k++;
}
else {
seq[(i + k) % n] > seq[(j + k) % n] ? i += k + 1 : j += k + 1;
if (i == j) i++;
k = 0;
}
}
int ans = min(i, j);
for (k = 0; k < n; k++) {
printf("%d ", seq[(ans + k) % n]);
}
return 0;
}