1.问题描述
给出一个长度为
n
n
n的字符串
S
S
S,其第
i
i
i个字符为
S
i
(
0
<
=
i
<
n
)
S_i(0<=i<n)
Si(0<=i<n)。
可以重复执行操作:移除第
0
0
0个字符,并将其插入到第
n
−
1
n-1
n−1个位置。
找出所有可以通过上述操作得到的字符串中字典序最小的字符串,这个字典序最小的字符串被称为字符串
S
S
S的最小表示。
2.算法流程
1.定义两个指针
i
,
j
i,j
i,j,定义变量
k
k
k,初始时,让
i
i
i指向位置
0
0
0,
j
j
j指向位置
1
1
1,
k
=
0
k=0
k=0。
2.比较第
i
+
k
i+k
i+k和第
j
+
k
j+k
j+k个字符,
(这里默认为
x
+
k
x+k
x+k为
(
x
+
k
)
m
o
d
n
(x+k)\mod n
(x+k)modn,
x
=
i
,
j
x=i,j
x=i,j)
若
S
i
+
k
=
S
j
+
k
S_{i+k}=S_{j+k}
Si+k=Sj+k,让
k
=
k
+
1
k=k+1
k=k+1。
若
S
i
+
k
>
S
j
+
k
S_{i+k}>S_{j+k}
Si+k>Sj+k,让
i
=
i
+
k
i=i+k
i=i+k,
k
=
0
k=0
k=0。
若
S
i
+
k
<
S
j
+
k
S_{i+k}<S_{j+k}
Si+k<Sj+k,让
j
=
j
+
k
j=j+k
j=j+k,
k
=
0
k=0
k=0。
3.若
i
=
j
i=j
i=j,让
i
=
i
+
1
i=i+1
i=i+1
4.若
i
>
=
n
∣
∣
j
>
=
n
∣
∣
k
>
=
n
i>=n||j>=n||k>=n
i>=n∣∣j>=n∣∣k>=n,
m
i
n
(
i
,
j
)
min(i,j)
min(i,j)即为字典序最小的字符串的开头。否则,回到2。
3.证明
1.当
k
=
n
k=n
k=n,因为算法保证
i
!
=
j
i!=j
i!=j,则可以证明字符串只由一个字符组成,那么任意
i
,
j
i,j
i,j都为最小表示。
2.当
S
i
+
k
!
=
S
j
+
k
S_{i+k}!=S_{j+k}
Si+k!=Sj+k,不妨假设
S
i
+
k
>
S
j
+
k
S_{i+k}>S_{j+k}
Si+k>Sj+k,则第
i
,
i
+
1
,
.
.
.
,
i
+
k
i,i+1,...,i+k
i,i+1,...,i+k个位置一定不是字典序最小的字符串,设第
p
p
p个位置为最小字符串
(
i
<
=
p
<
=
i
+
k
)
(i<=p<=i+k)
(i<=p<=i+k),则有
S
p
S
p
+
1
.
.
.
S
i
+
k
−
1
=
S
j
+
p
−
i
S
j
+
p
−
i
+
1
.
.
.
S
j
+
k
−
1
S_pS_{p+1}...S_{i+k-1}=S_{j+p-i}S_{j+p-i+1}...S_{j+k-1}
SpSp+1...Si+k−1=Sj+p−iSj+p−i+1...Sj+k−1,
S
i
+
k
>
S
j
+
k
S_{i+k}>S_{j+k}
Si+k>Sj+k
在
j
,
j
+
1
,
.
.
.
,
j
+
k
j,j+1,...,j+k
j,j+1,...,j+k中存在对应比其字典序更小的字符串。
3.又
i
,
j
i,j
i,j会把所有下标都扫描到,当
i
>
=
n
∣
∣
j
>
=
n
i>=n||j>=n
i>=n∣∣j>=n又必有
i
,
j
i,j
i,j中某个指针的值小于
n
n
n,而另一指针一定由于操作
2
2
2或操作
3
3
3而移动,故值小于
n
n
n的指针则为最小表示字符串的开头。
4.复杂度
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n),证明略。
5.代码
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
int n,a[N];
int sol(int n,int a[N])
{
if(n==1) return 0;
int i=0,j=1,k=0;
while(i<n&&j<n&&k<n)
{
if(i==j) i++;
else if(a[(i+k)%n]==a[(j+k)%n]) k++;
else if(a[(i+k)%n]>a[(j+k)%n]) i+=k+1,k=0;
else j+=k+1,k=0;
}
return min(i,j);
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
int ans=sol(n,a);
for(int i=0;i<n;i++)
printf(i==n-1?"%d\n":"%d ",a[(i+ans)%n]);
}