hdu5442 Favorite Donut 字符串最大表示法 不用kmp也不用后缀数组的解法

题目大意:

给定一个字符构成的环,在这个环中选定一个起点,顺时针或逆时针使得以选定点为起点的字符串字典序最大。如果有多个答案,优先选择使起点位置在原字符串中编号较小的,如果还有多个答案,优先选择顺时针。(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了,对比两次用时:

 

这样就解决了之前出现的那个问题啦!

欢迎指教。

posted @ 2015-09-16 12:54  Julyed  阅读(829)  评论(7编辑  收藏  举报