题目大意:
给定一个字符构成的环,在这个环中选定一个起点,顺时针或逆时针使得以选定点为起点的字符串字典序最大。如果有多个答案,优先选择使起点位置在原字符串中编号较小的,如果还有多个答案,优先选择顺时针。(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了,对比两次用时:
这样就解决了之前出现的那个问题啦!
欢迎指教。