HDU - 5442Favorite Donut 最大表示法+KMP/后缀数组

Posted lqllulu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU - 5442Favorite Donut 最大表示法+KMP/后缀数组相关的知识,希望对你有一定的参考价值。

题意

   给出一个长度为n的环状由小写字母组成的序列,请找出从何处断开,顺时针还是逆时针,使得字典序最大。如果两个字符串的字典序一样大,那么它会选择下下标最小的那个。如果某个点顺时针逆时针产生的字典序大小相同,那么优先选择顺时针的。

这个题用最大表示法+KMP很容易解决。因为最大表示法找到的是下表最小的那个字典序最大的字符串,所以正向的时候最大表示法找出来的就直接是答案,关键是逆时针的时候。我们将字符串翻转以后用最大表示法找到那个字符串s2,然后用KMP算法在翻转*2后的串中找出最后面的那个s2,这就是逆时针时候的答案。然后比较顺时针和逆时针时候的答案,选取最优的就可以了。

  下面是代码。

 

技术分享图片
  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #include <iostream>
  5 
  6 
  7 using namespace std;
  8 const int maxn=40000+100;
  9 char s[maxn],str[maxn];
 10 char s1[maxn],s2[maxn];
 11 int T,n,ans1,ans2;
 12 int get_max(char *S){
 13     int len=strlen(S);
 14     int i=0,j=1,k=0;
 15     while(i<len&&j<len&&k<len){
 16         int t=S[(i+k)%len]-S[(j+k)%len];
 17         if(t==0)k++;
 18         else{
 19             if(t>0)
 20                 j+=k+1;
 21             else
 22                 i+=k+1;
 23             if(i==j)j++;
 24             k=0;
 25         }
 26     }
 27     return min(i,j);
 28 }
 29 int f[maxn];
 30 void get_fail(char *P){
 31     int n=strlen(P);
 32     f[0]=0,f[1]=0;
 33     for(int i=1;i<n;i++){
 34         int j=f[i];
 35         while(j&&P[i]!=P[j])j=f[j];
 36         f[i+1]=P[i]==P[j]?j+1:0;
 37     }
 38 }
 39 int find(char *T,char *P){
 40     int res=0,len=strlen(T),m=strlen(P);
 41     get_fail(P);
 42     int j=0;
 43     for(int i=0;i<2*n;i++){
 44         while(T[i]!=P[j]&&j)j=f[j];
 45         if(T[i]==P[j])j++;
 46         if(j==m){
 47             if(i-m+1<n){
 48             res=i-m+1;
 49             }
 50         }
 51     }
 52     return res;
 53 }
 54 int main(){
 55     scanf("%d",&T);
 56     for(int t=1;t<=T;t++){
 57         scanf("%d",&n);
 58         scanf("%s",str);
 59         ans1=get_max(str);
 60         for(int i=0;i<n;i++){
 61             s1[i]=str[(ans1+i)%n];
 62         }
 63         s1[n]=0;
 64         reverse(str,str+n);
 65         int j=get_max(str);
 66         for(int i=0;i<n;i++){
 67             s2[i]=str[(j+i)%n];
 68         }
 69         s2[n]=0;
 70         for(int i=0;i<n;i++)
 71             str[i+n]=str[i];
 72         str[2*n]=0;
 73         int ans2=find(str,s2);
 74         int ok=0;
 75         ans2=n-ans2-1;
 76 //        printf("%d %d
",ans1,ans2);
 77 //        printf("%s
%s
",s1,s2);
 78 
 79         for(int i=0;i<n;i++){
 80             if(s1[i]>s2[i]){
 81                 ok=1;
 82                 break;
 83             }
 84             if(s1[i]<s2[i]){
 85                 ok=-1;
 86                 break;
 87             }
 88         }
 89         ans1++,ans2++;
 90         if(ok>0){
 91             printf("%d %d
",ans1,0);
 92         }
 93         else if(ok<0){
 94             printf("%d %d
",ans2,1);
 95         }
 96         else{
 97             if(ans2<ans1){
 98                 printf("%d %d
",ans2,1);
 99             }else if(ans1<ans2){
100                 printf("%d %d
",ans1,0);
101             }else{
102                 printf("%d %d
",ans1,0);
103             }
104         }
105     }
106 return 0;
107 }
View Code

这个题显然也可以用后缀数组来搞。跟上面的思路差不多,顺时针的时候可以直接用sa数组找出答案(因为前面的一定长于后面的),关键是逆时针的时候如何找出下标最大的最大字典序的字符串。我们可以利用一下height数组。当height[i]==len的时候,就是我们要的答案。

 

技术分享图片
  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #include <iostream>
  5 
  6 using namespace std;
  7 const int maxn=60000+10;
  8 int T,n;
  9 char s[maxn];
 10 char s1[maxn],s2[maxn];
 11 int sa[maxn],c[maxn],x[maxn],y[maxn],height[maxn],rak[maxn];
 12 void build_sa(int m){
 13     memset(y,-1,sizeof(y));
 14     for(int i=0;i<m;i++)c[i]=0;
 15     for(int i=0;i<n;i++)c[x[i]=s[i]]++;
 16     for(int i=1;i<m;i++)c[i]+=c[i-1];
 17     for(int i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
 18 
 19     for(int k=1;k<=n;k<<=1){
 20         int p=0;
 21         for(int i=n-k;i<n;i++)y[p++]=i;
 22         for(int i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k;
 23 
 24         for(int i=0;i<m;i++)c[i]=0;
 25         for(int i=0;i<n;i++)c[x[y[i]]]++;
 26         for(int i=1;i<m;i++)c[i]+=c[i-1];
 27         for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
 28         swap(x,y);
 29         p=1,x[sa[0]]=0;
 30         for(int i=1;i<n;i++)
 31             x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p-1:p++;
 32         if(p>=n)break;
 33         m=p;
 34     }
 35 }
 36 
 37 void getHeight(){
 38     int k=0;
 39     for(int i=0;i<n;i++)rak[sa[i]]=i;
 40     for(int i=0;i<n;i++){
 41         if(k)k--;
 42         int j=sa[rak[i]-1];
 43         while(s[i+k]==s[j+k])k++;
 44         height[rak[i]]=k;
 45     }
 46 }
 47 int R[maxn],num;
 48 char str[maxn];
 49 int ans1,ans2,len;
 50 int main(){
 51     scanf("%d",&T);
 52     for(int t=1;t<=T;t++){
 53         scanf("%d",&len);
 54         scanf("%s",str);
 55         for(int i=0;i<len;i++){
 56             s[i]=str[i];
 57             s[i+len]=str[i];
 58         }
 59         n=2*len;
 60         s[n]=0;
 61         build_sa(123);
 62         for(int i=n-1;i>=0;i--){
 63             if(sa[i]<len){
 64                 ans1=sa[i];
 65                 break;
 66             }
 67         }
 68         for(int i=0;i<len;i++)
 69             s1[i]=s[i+ans1];
 70         reverse(str,str+len);
 71         for(int i=0;i<len;i++){
 72             s[i]=str[i];
 73             s[i+len]=str[i];
 74         }
 75         s[n]=0;
 76         build_sa(123);
 77         getHeight();
 78 //        printf("%s
",s);
 79 //        for(int i=0;i<n;i++)
 80 //            printf("%d ",sa[i]);
 81 //        printf("
");
 82 //        for(int i=0;i<n;i++)
 83 //            printf("%d ",height[i]);
 84 //        printf("
");
 85         for(int i=n-1;i>=0;i--){
 86             if(sa[i]<len){
 87                 ans2=sa[i];
 88                 if(height[i]<=len)
 89                     break;
 90             }
 91         }
 92         for(int i=0;i<len;i++){
 93             s2[i]=s[i+ans2];
 94         }
 95         s2[len]=0;
 96         int ok=0;
 97         for(int i=0;i<len;i++){
 98             if(s1[i]>s2[i]){
 99                 ok=1;
100                 break;
101             }
102             if(s1[i]<s2[i]){
103                 ok=-1;
104                 break;
105             }
106         }
107         ans2=len-ans2-1;
108         ans1++,ans2++;
109         //printf("%d %d %d
",ans1,ans2,ok);
110         if(ok>0){
111             printf("%d 0
",ans1);
112         }else if(ok<0){
113             printf("%d 1
",ans2);
114         }else{
115             if(ans2<ans1){
116                 printf("%d 1
",ans2);
117             }else{
118         
View Code

 

以上是关于HDU - 5442Favorite Donut 最大表示法+KMP/后缀数组的主要内容,如果未能解决你的问题,请参考以下文章

HDU 2571 命运(简单DP)

HDU2188 悼念512汶川大地震遇难同胞——选拔志愿者

msf+Donut执行程序

Donut和MSF以shellcode注入的方式执行任意文件

A. Donut Shops1000 / 思维题

html [Donut spinner]创建一个甜甜圈微调器,可用于指示内容的加载。 #CSS