HDU 5442 Favorite Donut(暴力 or 后缀数组 or 最大表示法)

http://acm.hdu.edu.cn/showproblem.php?pid=5442

题意:
给出一串字符串,它是循环的,现在要选定一个起点,使得该字符串字典序最大(顺时针和逆时针均可),如果有多个字典序相同的,则输出下标最小的,如果下标也是相同的,则输出顺时针方向的。

 

思路:
用了三种方法:

①最简单的,暴力枚举所有情况,需要优化一下,先处理出字符串中字典序最大的单词,然后接下来的起点肯定是这个单词,否则就可以跳过这个起点。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 
 7 int n,mx;
 8 string s, best;
 9 
10 int main()
11 {
12    // freopen("in.txt","r",stdin);
13     int T;
14     scanf("%d",&T);
15     while(T--)
16     {
17         scanf("%d",&n);
18         cin>>s;
19         mx = -1;
20         for(int i=0;i<n;i++)
21             if(s[i]-'a' > mx)  mx = s[i]-'a';
22         s += s;
23 
24         int pos, dir;
25         best = "";
26         for(int i=0;i<n;i++)
27         {
28             if(s[i]-'a'!=mx)  continue;
29             string tmp = s.substr(i,n);
30             if(tmp > best)
31             {
32                 pos = i;
33                 dir = 0;
34                 best = tmp;
35             }
36         }
37         reverse(s.begin(),s.end());
38         for(int i=n-1;i>=0;i--)
39         {
40             if(s[i]-'a'!=mx)  continue;
41             string tmp = s.substr(i,n);
42             if(tmp > best)
43             {
44                 pos = n-1-i;
45                 dir = 1;
46                 best = tmp;
47             }
48             else if(tmp == best)
49             {
50                 if(n-1-i<pos)
51                 {
52                     pos = n-1-i;
53                     dir = 1;
54                 }
55             }
56         }
57         printf("%d %d\n",pos+1,dir);
58     }
59     return 0;
60 }
暴力枚举

②后缀数组:

正序复制一遍,求一遍后缀数组,那么sa数组中排名最后的肯定是字典序最大的,此时直接取该值即可。而且此时它一定是最小坐标。

但是求逆序的时候需要注意一下,此时就不能求最小坐标了,因为逆序了,所以最小坐标在原来的字符串中是最大的,所以此时就要求最大字典序情况下的最小坐标。那么此时就要利用height数组了,从底向上依次遍历sa数组,如果和上一个的公共前缀是>=n的话,此时字典序是相同的,但是上一个字符串的坐标更大。知道height值<n。

  1 #include<iostream>
  2 #include<algorithm>
  3 #include<cstring>
  4 #include<cstdio>
  5 using namespace std;
  6 const int maxn=40000+5;
  7 
  8 int n;
  9 char s[maxn];
 10 char s1[maxn],s2[maxn];
 11 int sa[maxn],t[maxn],t2[maxn],c[maxn];
 12 int Rank[maxn],height[maxn];
 13 
 14 void build_sa(int m)
 15 {
 16     int *x=t,*y=t2;
 17     //基数排序
 18     for(int i=0;i<m;i++)    c[i]=0;
 19     for(int i=0;i<n;i++)    c[x[i]=s[i]]++;
 20     for(int i=1;i<m;i++)    c[i]+=c[i-1];
 21     for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
 22     for(int k=1;k<=n;k<<=1)
 23     {
 24         int p=0;
 25         //直接利用sa数组排序第二关键字
 26         for(int i=n-k;i<n;i++)  y[p++]=i;
 27         for(int i=0;i<n;i++)    if(sa[i]>=k)    y[p++]=sa[i]-k;
 28         //基数排序第一关键字
 29         for(int i=0;i<m;i++)    c[i]=0;
 30         for(int i=0;i<n;i++)    c[x[y[i]]]++;
 31         for(int i=1;i<m;i++)    c[i]+=c[i-1];
 32         for(int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
 33         //根据sa和y计算新的x数组
 34         swap(x,y);
 35         p=1;
 36         x[sa[0]]=0;
 37         for(int i=1;i<n;i++)
 38             x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
 39         if(p>=n)
 40             break;
 41         m=p;                //下次基数排序的最大值
 42     }
 43 }
 44 
 45 void getHeight(int n)
 46 {
 47     int i,j,k=0;
 48     for(i=1;i<=n;i++)  Rank[sa[i]]=i;
 49     for(i=0;i<n;i++)
 50     {
 51         if(k)  k--;
 52         int j=sa[Rank[i]-1];
 53         while(s[i+k]==s[j+k])  k++;
 54         height[Rank[i]]=k;
 55     }
 56 }
 57 
 58 int main()
 59 {
 60     //freopen("in.txt","r",stdin);
 61     int T;
 62     scanf("%d",&T);
 63     while(T--)
 64     {
 65         scanf("%d",&n);
 66         scanf("%s",s);
 67         for(int i=0;i<n;i++)  s[i+n] = s[i];
 68         s[2*n] = '0';
 69         int tmp = n;
 70         n = 2*n+1;
 71         build_sa(128);
 72         getHeight(n-1);
 73         n = tmp;
 74 
 75         int pos1;
 76         memset(s1,0,sizeof s1);
 77         for(int i=2*n;i>=1;i--)
 78         {
 79             if(sa[i]<n)
 80             {
 81                 pos1 = sa[i];
 82                 break;
 83             }
 84         }
 85         strncpy(s1,s+pos1,n);
 86 
 87         reverse(s,s+2*n);
 88         tmp = n;
 89         n = 2*n+1;
 90         build_sa(128);
 91         getHeight(n-1);
 92         n = tmp;
 93 
 94         int pos2;
 95         memset(s2,0,sizeof s2);
 96         for(int i=2*n;i>=1;i--)
 97         {
 98             if(sa[i]<n)
 99             {
100                 while(height[i]>n) i--;
101                 pos2 = n-1-sa[i];
102                 break;
103             }
104         }
105         strncpy(s2,s+n-1-pos2,n);
106 
107         if(strcmp(s1,s2)>0)  printf("%d 0\n",pos1+1);
108         else if(strcmp(s1,s2)<0) printf("%d 1\n",pos2+1);
109         else
110         {
111             if(pos1<=pos2)  printf("%d 0\n",pos1+1);
112             else printf("%d 1\n",pos2+1);
113         }
114     }
115     return 0;
116 }
后缀数组

③最大表示法:

参考论文:https://wenku.baidu.com/view/c6c5e7335a8102d276a22fa6.html

算法的具体思路: 
(1)开始时,将字符串复制两份,设置两个指针,i=0,j=1. 
(2)k=0,然后反复迭代直到s[i+k]!=s[j+k] 
(3)如果k=n,那么返回较小的值,否则看情况滑动指针 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 const int maxn = 40000+5;
 7 
 8 int n;
 9 char s[maxn];
10 char s1[maxn],s2[maxn];
11 
12 int solve1(char *x)  //最大表示法坐标最小
13 {
14     int i = 0, j = 1, k = 0;
15     while(i<n && j<n && k<n)
16     {
17         int t = x[i+k] - x[j+k];
18         if(!t)  k++;
19         else
20         {
21             if(t>0)  j+=k+1;
22             else i+=k+1;
23             if(i==j) j++;
24             k=0;
25         }
26     }
27     return i<j?i:j;
28 }
29 
30 int solve2(char *x)  //最大表示法且坐标最大
31 {
32     int i = 0, j = 1, k;
33     while(i < n && j < n)
34     {
35         while(x[i+k] == x[j+k] && k < n) k++;
36         if (k == n)  //这个意思就是以i开头的和以j开头的字符串相同
37         {
38             int len = abs(i - j);  //len的长度就是循环节
39             return n - len + i;   //取坐标最大的
40         }
41         else
42         {
43             int t = x[i+k] - x[j+k];
44             if(t>0)  j+=k+1;
45             else i+=k+1;
46             if(i==j) j++;
47             k = 0;
48         }
49     }
50     if(j >= n) return i;
51     return j;
52 }
53 
54 int main()
55 {
56     //freopen("in.txt","r",stdin);
57     int T;
58     scanf("%d",&T);
59     while(T--)
60     {
61         scanf("%d",&n);
62         scanf("%s",s);
63         for(int i=0;i<n;i++)  s[i+n] = s[i];
64         s[2*n] = '\0';
65 
66         int pos1 = solve1(s);
67         memset(s1,0,sizeof s1);
68         strncpy(s1,s+pos1,n);
69 
70         reverse(s,s+2*n);
71         int pos2 = solve2(s);
72         memset(s2,0,sizeof s2);
73         strncpy(s2,s+pos2,n);
74 
75         int t = strcmp(s1,s2);
76         if(t>0)  printf("%d 0\n",pos1+1);
77         else if(t<0)  printf("%d 1\n",n-pos2);
78         else
79         {
80             if(pos1+1<=n-pos2)  printf("%d 0\n",pos1+1);
81             else printf("%d 1\n",n-pos2);
82         }
83     }
84     return 0;
85 }
最大表示法

 

 

posted @ 2017-11-30 21:22  Kayden_Cheung  阅读(174)  评论(0编辑  收藏  举报
//目录