简介

最小表示法,求所有与某个字符串循环同构的字符串中,字典序最小的那个。比如说一个字符串lordash,它长度为7,也就是说最多有七种循环同构的方法。

lordash、ordashl、rdashlo、dashlor、ashlord、shlorda、hlordas。

这几个串在原串上的开始位置分别是0,1,2,3,4,5,6。字典序最小的同构即是以4为起点的那个。

朴素算法

给出一个朴素的算法,我们每次比较i和j开始的循环同构,把当前比较到的位置记作k,每次遇到不一样的字符时便把大的跳过,最后剩下的就是最优解。最坏时间复杂度$ O(|S|^{2}) $。

实现代码如下:

 1int simple(char* s, int n)
 2{
 3    int i=0, j=1, k=0;
 4    while(i<n && j<n && k<n)
 5    {
 6        if(s[(i+k)%n] == s[(j+k)%n]){
 7            k++;
 8        }else{
 9            if(s[(i+k)%n] > s[(j+k)%n])
10                i++;
11            else
12                j++;
13            k = 0;
14            if(i == j) i++;
15        }
16    }
17    return min(i, j);
18}

最小表示法O(|S|)

如果比较起始位置$i$和起始位置$j$发现$S[i,i+1,\ldots,i+k-1]=S[j,j+1,\ldots,j+k-1]$且$S[i+k] \lt S[j+k]$,则起始位置$j,j+1,\ldots,j+k$都不合法。对于每个数字$0 \le l \le k$都有起始位置$i+l$比起始位置$j+l$优,因为$S[i+l,i+l+1, \ldots,i+k-1]=S[j+l,j+l+1,\ldots,j+k-1]$且$S[i+k] \lt S[j+k]$ 。

代码如下:

 1int getmin(char* s, int n)
 2{
 3    int i=0, j=1, k=0;
 4    while(i<n && j<n && k<n)
 5    {
 6        if(s[(i+k)%n] == s[(j+k)%n]){
 7            k++;
 8        }else{
 9            if(s[(i+k)%n] > s[(j+k)%n])
10                i+=k+1;
11            else
12                j+=k+1;
13            k = 0;
14            if(i == j) i++;
15        }
16    }
17    return min(i, j);
18}

模板

 1char s[mxn];
 2
 3int simple(char* s, int n)
 4{
 5    int i=0, j=1, k=0;
 6    while(i<n && j<n && k<n)
 7    {
 8        if(s[(i+k)%n] == s[(j+k)%n]){
 9            k++;
10        }else{
11            if(s[(i+k)%n] > s[(j+k)%n])
12                i++;
13            else
14                j++;
15            k = 0;
16            if(i == j) i++;
17        }
18    }
19    return min(i, j);
20}
21
22int getmin(char* s, int n)
23{
24    int i=0, j=1, k=0;
25    while(i<n && j<n && k<n)
26    {
27        if(s[(i+k)%n] == s[(j+k)%n]){
28            k++;
29        }else{
30            if(s[(i+k)%n] > s[(j+k)%n])
31                i+=k+1;
32            else
33                j+=k+1;
34            k = 0;
35            if(i == j) i++;
36        }
37    }
38    return min(i, j);
39}
40
41int main()
42{
43    scanf("%s", s);
44    int n = strlen(s);
45    printf("%d\n", simple(s, n));
46    printf("%d\n", getmin(s, n));
47    return 0;
48}