【HDU - 5442】Favorite 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\n",ans1,ans2);
 77 //        printf("%s\n%s\n",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\n",ans1,0);
 92         }
 93         else if(ok<0){
 94             printf("%d %d\n",ans2,1);
 95         }
 96         else{
 97             if(ans2<ans1){
 98                 printf("%d %d\n",ans2,1);
 99             }else if(ans1<ans2){
100                 printf("%d %d\n",ans1,0);
101             }else{
102                 printf("%d %d\n",ans1,0);
103             }
104         }
105     }
106 return 0;
107 }

这个题显然也可以用后缀数组来搞。跟上面的思路差不多,顺时针的时候可以直接用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\n",s);
 79 //        for(int i=0;i<n;i++)
 80 //            printf("%d ",sa[i]);
 81 //        printf("\n");
 82 //        for(int i=0;i<n;i++)
 83 //            printf("%d ",height[i]);
 84 //        printf("\n");
 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\n",ans1,ans2,ok);
110         if(ok>0){
111             printf("%d 0\n",ans1);
112         }else if(ok<0){
113             printf("%d 1\n",ans2);
114         }else{
115             if(ans2<ans1){
116                 printf("%d 1\n",ans2);
117             }else{
118         

原文地址:https://www.cnblogs.com/LQLlulu/p/9536475.html

时间: 2024-10-09 20:38:17

【HDU - 5442】Favorite Donut 【最大表示法+KMP/后缀数组】的相关文章

hdu 5442 Favorite Donut 最大表示法+KMP

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5442 题意: 有一个由小写字母组成的字符串(长度为n),首尾相接,求顺时针转和逆时针转的情况下,长度为n的最大字典序的字符串的首位的位置. 如果顺时针和逆时针求得的字符串相同,则选择开始位置较前的,如果开始位置也相同,则选择顺时针的. 如abcd,那么顺时针可以是abcd,bcda,cdab,dabc.逆时针可以是adcb,dcba,cbad,badc. 思路: 顺时针的情况下,直接求最大字典序的位

HDU 5442——Favorite Donut——————【最大表示法+kmp | 后缀数组】

Favorite Donut Time Limit: 1500/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 1702    Accepted Submission(s): 430 Problem Description Lulu has a sweet tooth. Her favorite food is ring donut. Everyday she buy

Hdu 5442 Favorite Donut (2015 ACM/ICPC Asia Regional Changchun Online 最大最小表示法 + KMP)

题目链接: Hdu 5442 Favorite Donut 题目描述: 给出一个文本串,找出顺时针或者逆时针循环旋转后,字典序最大的那个字符串,字典序最大的字符串如果有多个,就输出下标最小的那个,如果顺时针和逆时针的起始下标相同,则输出顺时针. 解题思路: 看到题目感觉后缀数组可以搞,正准备犯傻被队友拦下了,听队友解释一番,果断丢锅给队友.赛后试了一下后缀数组果然麻烦的不要不要的(QWQ),还是最大最小表示法 + KMP来的干净利索. 最大表示法:对于一个长度为len文本串,经过循环旋转得到长度

hdu 5442 Favorite Donut 后缀数组

Favorite Donut Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=5442 Description Lulu has a sweet tooth. Her favorite food is ring donut. Everyday she buys a ring donut from the same bakery. A ring donut is consis

luogu 2463 [SDOI2008]Sandy的卡片 kmp || 后缀数组 n个串的最长公共子串

题目链接 Description 给出\(n\)个序列.找出这\(n\)个序列的最长相同子串. 在这里,相同定义为:两个子串长度相同且一个串的全部元素加上一个数就会变成另一个串. 思路 参考:hzwer. 法一:kmp 在第一个串中枚举答案串的开头位置,与其余\(n-1\)个串做\(kmp\). 法二:后缀数组 将\(n\)个串拼接起来.二分答案\(len\),将\(height\)分组,\(check\)是否有一组个数\(\geq len\)且落在\(n\)个不同的串中. 注意:\(n\)个串

POJ-3450 Corporate Identity (KMP+后缀数组)

Description Beside other services, ACM helps companies to clearly state their "corporate identity", which includes company logo but also other signs, like trademarks. One of such companies is Internet Building Masters (IBM), which has recently a

POJ2406 Power Strings(KMP,后缀数组)

这题可以用后缀数组,KMP方法做 后缀数组做法开始想不出来,看的题解,方法是枚举串长len的约数k,看lcp(suffix(0), suffix(k))的长度是否为n- k ,若为真则len / k即为结果. 若lcp(suffix(0), suffix(k))的长度为n- k,则将串每k位分成一段,则第1段与第2段可匹配,又可推得第2段与第3段可匹配……一直递归下去,可知每k位都是相同的,画图可看出匹配过程类似于蛇形. 用倍增算法超时,用dc3算法2.5秒勉强过. #include<cstdi

用倍增法构造后缀数组中的SA及RANK数组

感觉后缀数组很难学的说= = 不过总算是啃下来了 首先 我们需要理解一下倍增法构造的原理 设原串的长度为n 对于每个子串 我们将它用'\0'补成长度为2^k的串(2^k-1<n<=2^k) 比如串aba的子串就有 aba'\0'    ba'\0''\0'  a'\0''\0''\0' 每次操作我们可以排出所有长度为 2^x的子串的大小 比如串aba的排序过程 第一遍 a                   a             b 第二遍 a'\0'             ab  

POJ 2406 KMP/后缀数组

题目链接:http://poj.org/problem?id=2406 题意:给定一个字符串,求由一个子串循环n次后可得到原串,输出n[即输出字符串的最大循环次数] 思路一:KMP求最小循环机,然后就能求出循环次数. #define _CRT_SECURE_NO_DEPRECATE #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<str