題目大意:
給定一個字符構成的環,在這個環中選定一個起點,順時針或逆時針使得以選定點為起點的字符串字典序最大。如果有多個答案,優先選擇使起點位置在原字符串中編號較小的,如果還有多個答案,優先選擇順時針。(tomriddly親手寫的題意)
思路:
字符串最大表示法。存兩個二倍字符串,一個正向一個反向。對於正向字符串,用get_mnstring()找到最大字符串的最小下標ans1;對於反向字符串,用get_mxstring()找到最大字符串的最大下標ans2(難點)。然后將通過這兩個起點得到的字符串分別保存起來,用strcmp()比較一下。哪個比較大,哪個下標就是正確答案。一樣大的時候,比較一下兩個起點的大小,比較小的是正確答案,如果還是一樣大,則輸出正向。還有一點,ans2要想辦法轉化成原字符串的下標。
寫字符串最大表示法的時候參考了此牛的博客。http://blog.csdn.net/zy691357966/article/details/39854359
新增:有人提出get_mxstring()的方法有問題,即當字符串為循環節較小的循環串的時候(e.g.aaaaaaaaaaa,ababababab),時間復雜度會升到O(n^2)。所以此方法是不完全正確的。但是看到很多人的做法是用kmp,這個貌似也是有同樣的弊端的吧。如果有人有不同意見,歡迎各種姿勢提出來,或者有好的改良方案也麻煩告訴我一下~~謝謝啦。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 6 using namespace std; 7 8 const int MAXN = 20000 + 11; 9 10 char s[MAXN * 2], sfan[MAXN * 2], sans1[MAXN], sans2[MAXN]; 11 int n; 12 13 int get_mnstring(char *x) 14 { 15 int i = 0, j = 1, k = 0; 16 while(i < n && j < n) 17 { 18 k = 0; 19 while (x[i + k] == x[j + k] && k < n) 20 k++; 21 if (k == n) 22 return min(i, j); 23 if (x[i + k] < x[j + k]) 24 if (i + k + 1 > j) 25 i = i + k + 1; 26 else 27 i = j + 1; 28 else if (j + k + 1 > i) 29 j = j + k + 1; 30 else 31 j = i + 1; 32 } 33 return min(i, j); 34 } 35 36 int get_mxstring(char *x) 37 { 38 int i = 0, j = 1, k; 39 while(i < n && j < n) 40 { 41 k = 0; 42 while(x[i + k] == x[j + k] && k < n) 43 k++; 44 /*有問題的部分 45 if(k == n) 46 { 47 i = max(i, j); //不返回,保存較大下標,重新找 48 j = i + 1; 49 }*/ 50 if (k == n) 51 { 52 int len = abs(i - j); 53 return n - len + i; 54 } 55 else 56 { 57 if (x[i + k] < x[j + k]) 58 if (i + k + 1 > j) 59 i = i + k + 1; 60 else 61 i = j + 1; 62 else if (j + k + 1 > i) 63 j = j + k + 1; 64 else 65 j = i + 1; 66 } 67 } 68 if(j >= n) 69 return i; 70 return j; 71 } 72 73 74 75 76 int main() 77 { 78 int t; 79 scanf("%d", &t); 80 while(t--) 81 { 82 scanf("%d", &n); 83 scanf("%s", s); 84 for(int i = 0; i < n; i++) 85 s[i + n] = sfan[n - i - 1] = sfan[2 * n - i - 1] = s[i]; 86 s[n + n] = sfan[n + n] = '\0'; 87 // printf("%s\n%s\n", s, sfan); 88 int ans1 = get_mnstring(s), ans2 = get_mxstring(sfan); 89 //printf("%d %d\n", ans1, ans2); 90 91 for(int i = 0; i < n; i++) 92 { 93 sans1[i] = s[ans1 + i]; 94 sans2[i] = sfan[ans2 + i]; 95 } 96 sans1[n] = sans2[n] = '\0'; 97 // printf("%s\n%s\n", sans1, sans2); 98 if(strcmp(sans2, sans1) > 0) 99 printf("%d 1\n", n - ans2); 100 else if(strcmp(sans2, sans1) < 0) 101 printf("%d 0\n", ans1 + 1); 102 else 103 { 104 if(n -ans2 < ans1 + 1) 105 printf("%d 1\n", n - ans2); //ans2在原字符串中的下標 106 else 107 printf("%d 0\n", ans1 + 1); 108 } 109 110 } 111 return 0; 112 }
新增: 洗臉突然想到的。。。。對於函數get_mxstring(),可以在第一次找到k==n的時候計算出循環節,即循環節len = abs(i - j);然后直接計算出返回位置。
即:
1 if (k == n) 2 { 3 int len = abs(i - j); 4 return n - len + i; 5 }
提交后也AC了,對比兩次用時:
這樣就解決了之前出現的那個問題啦!
歡迎指教。