简介
最小表示法,求所有与某个字符串循环同构的字符串中,字典序最小的那个。比如说一个字符串lordash,它长度为7,也就是说最多有七种循环同构的方法。
lordash、ordashl、rdashlo、dashlor、ashlord、shlorda、hlordas。
这几个串在原串上的开始位置分别是0,1,2,3,4,5,6。字典序最小的同构即是以4为起点的那个。
朴素算法
给出一个朴素的算法,我们每次比较i和j开始的循环同构,把当前比较到的位置记作k,每次遇到不一样的字符时便把大的跳过,最后剩下的就是最优解。最坏时间复杂度$ O(|S|^{2}) $。
实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| int simple(char* s, int n) { int i=0, j=1, k=0; while(i<n && j<n && k<n) { if(s[(i+k)%n] == s[(j+k)%n]){ k++; }else{ if(s[(i+k)%n] > s[(j+k)%n]) i++; else j++; k = 0; if(i == j) i++; } } return min(i, j); }
|
最小表示法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]\) 。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| int getmin(char* s, int n) { int i=0, j=1, k=0; while(i<n && j<n && k<n) { if(s[(i+k)%n] == s[(j+k)%n]){ k++; }else{ if(s[(i+k)%n] > s[(j+k)%n]) i+=k+1; else j+=k+1; k = 0; if(i == j) i++; } } return min(i, j); }
|
模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| char s[mxn];
int simple(char* s, int n) { int i=0, j=1, k=0; while(i<n && j<n && k<n) { if(s[(i+k)%n] == s[(j+k)%n]){ k++; }else{ if(s[(i+k)%n] > s[(j+k)%n]) i++; else j++; k = 0; if(i == j) i++; } } return min(i, j); }
int getmin(char* s, int n) { int i=0, j=1, k=0; while(i<n && j<n && k<n) { if(s[(i+k)%n] == s[(j+k)%n]){ k++; }else{ if(s[(i+k)%n] > s[(j+k)%n]) i+=k+1; else j+=k+1; k = 0; if(i == j) i++; } } return min(i, j); }
int main() { scanf("%s", s); int n = strlen(s); printf("%d\n", simple(s, n)); printf("%d\n", getmin(s, n)); return 0; }
|