简介
最小表示法,求所有与某个字符串循环同构的字符串中,字典序最小的那个。比如说一个字符串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}