题意
题解
求字符串中字典序最小与最大的循环同构串,并求解对应的起始字符在原字符串中出现的位置最早的一个,以及这个循环同构串出现的次数。
最小/大表示法
字符串字典序最小/大的循环同构串称为字符串的最小/大表示。设字符串 S S S 长度为 n n n,最小/大表示法的可以 O ( n ) O(n) O(n) 求解,区别只在于字典序的判断。以最小表示法的求解为例,将原串 S S S 复制一份接在其结尾,得到的字符串记为 S S SS SS,则有 S S [ i , i + n ) , i ∈ [ 0 , n ) SS[i, i+n),i\in[0, n) SS[i,i+n),i∈[0,n) 为 S S S 的以 i i i 为起始位的循环同构串。
对于任意的 i , j i,j i,j,观察其字典序比较过程, 设 k ( k < n ) k(k<n) k(k<n) 为以 i , j i,j i,j 为起始位置的循环同构串的最长前缀匹配长度,若 S S [ i + k ] > S S [ j + k ] SS[i+k]>SS[j+k] SS[i+k]>SS[j+k],那么对于以位置 [ i , i + k ] [i,i+k] [i,i+k] 为起始位置的循环同构串,都存在对应的起始位置 [ j , j + k ] [j,j+k] [j,j+k] 的字典序更小的循环同构串,跳过这些位置,一定不会遗漏最小表示的起始位置。于是初始化 i = 0 , j = 1 i=0,j=1 i=0,j=1,不断比较以 i , j i,j i,j 为起始位置的循环同构串,若 k < n k<n k<n,则按照上述规则不断跳过不可能为备选答案的位置,最终当 i ≤ n i\leq n i≤n 或 j ≤ n j\leq n j≤n 时,所有循环同构串都进行过比较,那么 m i n ( i , j ) min(i,j) min(i,j) 即最小表示的起始位置;若出现 k = n k=n k=n 的情况,说明 S S S 有更小的循环元,由于 i , j i,j i,j 从左向右扫描,则 i , j i,j i,j 分别为最小表示的最小起始位置和次小起始位置,则 m a x ( i , j ) − m i n ( i , j ) max(i,j)-min(i,j) max(i,j)−min(i,j) 为最小循环元的长度。
那么,若求解最小表示法时出现 k = n k=n k=n,那么 m i n ( i , j ) min(i,j) min(i,j) 为最小表示对应的起始字符在原字符串中出现的位置最早的一个,出现次数为 n / [ m a x ( i , j ) − m i n ( i , j ) ] n/[max(i,j)-min(i,j)] n/[max(i,j)−min(i,j)];反之, S S S 不存在更小的循环元, m i n ( i , j ) min(i,j) min(i,j) 为最小表示对应的起始字符在原字符串中出现的位置最早的一个,出现次数为 1 1 1。
循环元存在性证明
方便起见,设字符串长度为 x x x,设 i , j i,j i,j 从左向右扫描出现的最早满足 k = x k=x k=x 情况的下标分别为 0 0 0 和 y , y ∈ [ 1 , x ) y,y\in[1,x) y,y∈[1,x),对应的循环同构串为 A , B A,B A,B。
先证必要性。若 S S S 存在长度小于 x x x 的循环元,设其长度为 y y y,那么以 k t , t ∈ [ 0 , x / y ) kt,t\in [0,x/y) kt,t∈[0,x/y) 为起始位置的循环同构串相等,则比较字典序时出现 k = x k=x k=x 情况。
再证充分性,若
y
∣
x
y\mid x
y∣x,此时
A
[
n
−
y
,
n
)
=
B
[
n
−
2
y
,
n
−
y
)
=
A
[
n
−
2
y
,
n
−
y
)
A[n-y,n)=B[n-2y,n-y)=A[n-2y,n-y)
A[n−y,n)=B[n−2y,n−y)=A[n−2y,n−y) 递推到边界,可以观察到
A
,
B
A, B
A,B 是按照循环节的长度交错排列的,且存在长度为
y
y
y 的循环节;若
y
∤
x
y\nmid x
y∤x,按照上述思路递推,在边界上有
A
[
0
,
y
)
=
S
[
0
,
y
)
=
B
[
n
−
y
,
n
)
=
B
[
x
%
y
,
x
%
y
+
y
)
=
A
[
x
%
y
,
x
%
y
+
y
)
A[0,y)=S[0,y)=B[n-y,n)=B[x\% y,x\%y+y)=A[x\%y,x\%y+y)
A[0,y)=S[0,y)=B[n−y,n)=B[x%y,x%y+y)=A[x%y,x%y+y) 设
x
′
=
y
,
y
′
=
x
%
y
x'=y,y'=x\%y
x′=y,y′=x%y,则得到规模缩小的原问题。设
f
(
x
,
y
)
f(x,y)
f(x,y) 代表字符串长度为
x
x
x,
2
2
2 个循环同构串起始位置索引差值为
y
y
y 且相等时原串
S
S
S 是否存在长度小于
x
x
x 的循环节,那么有
f
(
x
,
y
)
=
f
(
y
,
x
%
y
)
f(x,y)=f(y,x\%y)
f(x,y)=f(y,x%y) 递归问题的形式与欧几里德算法一致,边界情况
f
(
g
c
d
(
x
,
y
)
,
0
)
f(gcd(x,y),0)
f(gcd(x,y),0) 不满足
y
∈
[
1
,
x
)
y\in[1,x)
y∈[1,x),但可以推出递归的前一步为真,以其为递归的终点,则可以判断
f
(
x
,
y
)
f(x,y)
f(x,y) 为真,且循环节长度为
g
c
d
(
x
,
y
)
gcd(x,y)
gcd(x,y)。
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1000005;
char S[maxn * 2];
int n, rnk, t;
void solve(bool f)
{
int i = 0, j = 1, k;
while (i < n && j < n)
{
for (k = 0; k < n && S[i + k] == S[j + k]; ++k)
;
if (k == n)
{
rnk = min(i, j), t = n / (max(i, j) - rnk);
return;
}
bool g = (!f && S[i + k] > S[j + k]) || (f && S[i + k] < S[j + k]);
if (g)
i += k + 1, i += i == j;
else
j += k + 1, j += i == j;
}
rnk = min(i, j), t = 1;
}
int main()
{
while (~scanf(" %s", S))
{
n = strlen(S);
memcpy(S + n, S, sizeof(char) * n);
solve(0);
printf("%d %d ", rnk + 1, t);
solve(1);
printf("%d %d\n", rnk + 1, t);
}
return 0;
}