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了,對比兩次用時:

 

這樣就解決了之前出現的那個問題啦!

歡迎指教。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM