【HDU - 5442】Favorite Donut 【最大表示法+KMP/后缀数组】
题意
给出一个长度为n的环状由小写字母组成的序列,请找出从何处断开,顺时针还是逆时针,使得字典序最大。如果两个字符串的字典序一样大,那么它会选择下下标最小的那个。如果某个点顺时针逆时针产生的字典序大小相同,那么优先选择顺时针的。
这个题用最大表示法+KMP很容易解决。因为最大表示法找到的是下表最小的那个字典序最大的字符串,所以正向的时候最大表示法找出来的就直接是答案,关键是逆时针的时候。我们将字符串翻转以后用最大表示法找到那个字符串s2,然后用KMP算法在翻转*2后的串中找出最后面的那个s2,这就是逆时针时候的答案。然后比较顺时针和逆时针时候的答案,选取最优的就可以了。
下面是代码。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream> using namespace std;
const int maxn=+;
char s[maxn],str[maxn];
char s1[maxn],s2[maxn];
int T,n,ans1,ans2;
int get_max(char *S){
int len=strlen(S);
int i=,j=,k=;
while(i<len&&j<len&&k<len){
int t=S[(i+k)%len]-S[(j+k)%len];
if(t==)k++;
else{
if(t>)
j+=k+;
else
i+=k+;
if(i==j)j++;
k=;
}
}
return min(i,j);
}
int f[maxn];
void get_fail(char *P){
int n=strlen(P);
f[]=,f[]=;
for(int i=;i<n;i++){
int j=f[i];
while(j&&P[i]!=P[j])j=f[j];
f[i+]=P[i]==P[j]?j+:;
}
}
int find(char *T,char *P){
int res=,len=strlen(T),m=strlen(P);
get_fail(P);
int j=;
for(int i=;i<*n;i++){
while(T[i]!=P[j]&&j)j=f[j];
if(T[i]==P[j])j++;
if(j==m){
if(i-m+<n){
res=i-m+;
}
}
}
return res;
}
int main(){
scanf("%d",&T);
for(int t=;t<=T;t++){
scanf("%d",&n);
scanf("%s",str);
ans1=get_max(str);
for(int i=;i<n;i++){
s1[i]=str[(ans1+i)%n];
}
s1[n]=;
reverse(str,str+n);
int j=get_max(str);
for(int i=;i<n;i++){
s2[i]=str[(j+i)%n];
}
s2[n]=;
for(int i=;i<n;i++)
str[i+n]=str[i];
str[*n]=;
int ans2=find(str,s2);
int ok=;
ans2=n-ans2-;
// printf("%d %d\n",ans1,ans2);
// printf("%s\n%s\n",s1,s2); for(int i=;i<n;i++){
if(s1[i]>s2[i]){
ok=;
break;
}
if(s1[i]<s2[i]){
ok=-;
break;
}
}
ans1++,ans2++;
if(ok>){
printf("%d %d\n",ans1,);
}
else if(ok<){
printf("%d %d\n",ans2,);
}
else{
if(ans2<ans1){
printf("%d %d\n",ans2,);
}else if(ans1<ans2){
printf("%d %d\n",ans1,);
}else{
printf("%d %d\n",ans1,);
}
}
}
return ;
}
这个题显然也可以用后缀数组来搞。跟上面的思路差不多,顺时针的时候可以直接用sa数组找出答案(因为前面的一定长于后面的),关键是逆时针的时候如何找出下标最大的最大字典序的字符串。我们可以利用一下height数组。当height[i]==len的时候,就是我们要的答案。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream> using namespace std;
const int maxn=+;
int T,n;
char s[maxn];
char s1[maxn],s2[maxn];
int sa[maxn],c[maxn],x[maxn],y[maxn],height[maxn],rak[maxn];
void build_sa(int m){
memset(y,-,sizeof(y));
for(int i=;i<m;i++)c[i]=;
for(int i=;i<n;i++)c[x[i]=s[i]]++;
for(int i=;i<m;i++)c[i]+=c[i-];
for(int i=n-;i>=;i--)sa[--c[x[i]]]=i; for(int k=;k<=n;k<<=){
int p=;
for(int i=n-k;i<n;i++)y[p++]=i;
for(int i=;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k; for(int i=;i<m;i++)c[i]=;
for(int i=;i<n;i++)c[x[y[i]]]++;
for(int i=;i<m;i++)c[i]+=c[i-];
for(int i=n-;i>=;i--)sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=,x[sa[]]=;
for(int i=;i<n;i++)
x[sa[i]]=y[sa[i]]==y[sa[i-]]&&y[sa[i]+k]==y[sa[i-]+k]?p-:p++;
if(p>=n)break;
m=p;
}
} void getHeight(){
int k=;
for(int i=;i<n;i++)rak[sa[i]]=i;
for(int i=;i<n;i++){
if(k)k--;
int j=sa[rak[i]-];
while(s[i+k]==s[j+k])k++;
height[rak[i]]=k;
}
}
int R[maxn],num;
char str[maxn];
int ans1,ans2,len;
int main(){
scanf("%d",&T);
for(int t=;t<=T;t++){
scanf("%d",&len);
scanf("%s",str);
for(int i=;i<len;i++){
s[i]=str[i];
s[i+len]=str[i];
}
n=*len;
s[n]=;
build_sa();
for(int i=n-;i>=;i--){
if(sa[i]<len){
ans1=sa[i];
break;
}
}
for(int i=;i<len;i++)
s1[i]=s[i+ans1];
reverse(str,str+len);
for(int i=;i<len;i++){
s[i]=str[i];
s[i+len]=str[i];
}
s[n]=;
build_sa();
getHeight();
// printf("%s\n",s);
// for(int i=0;i<n;i++)
// printf("%d ",sa[i]);
// printf("\n");
// for(int i=0;i<n;i++)
// printf("%d ",height[i]);
// printf("\n");
for(int i=n-;i>=;i--){
if(sa[i]<len){
ans2=sa[i];
if(height[i]<=len)
break;
}
}
for(int i=;i<len;i++){
s2[i]=s[i+ans2];
}
s2[len]=;
int ok=;
for(int i=;i<len;i++){
if(s1[i]>s2[i]){
ok=;
break;
}
if(s1[i]<s2[i]){
ok=-;
break;
}
}
ans2=len-ans2-;
ans1++,ans2++;
//printf("%d %d %d\n",ans1,ans2,ok);
if(ok>){
printf("%d 0\n",ans1);
}else if(ok<){
printf("%d 1\n",ans2);
}else{
if(ans2<ans1){
printf("%d 1\n",ans2);
}else{
【HDU - 5442】Favorite Donut 【最大表示法+KMP/后缀数组】的更多相关文章
- hdu 5442 Favorite Donut 最大表示法+kmp
题目链接 给你一个字符串, 然后把他想象成一个环. 从某一个地方断开,然后逆时针或顺时针, 都可以形成一个字符串, 求字典序最大的那种. 输出断开位置以及是顺时针还是逆时针. 如果两个一样, 输出位置 ...
- HDU 5442——Favorite Donut——————【最大表示法+kmp | 后缀数组】
Favorite Donut Time Limit: 1500/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) ...
- Hdu 5442 Favorite Donut (2015 ACM/ICPC Asia Regional Changchun Online 最大最小表示法 + KMP)
题目链接: Hdu 5442 Favorite Donut 题目描述: 给出一个文本串,找出顺时针或者逆时针循环旋转后,字典序最大的那个字符串,字典序最大的字符串如果有多个,就输出下标最小的那个,如果 ...
- HDU 5442 Favorite Donut(暴力 or 后缀数组 or 最大表示法)
http://acm.hdu.edu.cn/showproblem.php?pid=5442 题意:给出一串字符串,它是循环的,现在要选定一个起点,使得该字符串字典序最大(顺时针和逆时针均可),如果有 ...
- hdu 5442 Favorite Donut 后缀数组
Favorite Donut Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid ...
- luogu 2463 [SDOI2008]Sandy的卡片 kmp || 后缀数组 n个串的最长公共子串
题目链接 Description 给出\(n\)个序列.找出这\(n\)个序列的最长相同子串. 在这里,相同定义为:两个子串长度相同且一个串的全部元素加上一个数就会变成另一个串. 思路 参考:hzwe ...
- POJ2406 Power Strings(KMP,后缀数组)
这题可以用后缀数组,KMP方法做 后缀数组做法开始想不出来,看的题解,方法是枚举串长len的约数k,看lcp(suffix(0), suffix(k))的长度是否为n- k ,若为真则len / k即 ...
- POJ 2406 KMP/后缀数组
题目链接:http://poj.org/problem?id=2406 题意:给定一个字符串,求由一个子串循环n次后可得到原串,输出n[即输出字符串的最大循环次数] 思路一:KMP求最小循环机,然后就 ...
- POJ-3450 Corporate Identity (KMP+后缀数组)
Description Beside other services, ACM helps companies to clearly state their “corporate identity”, ...
随机推荐
- smarty核心思想 自制模板引擎
<?php $tit = '今天下雨了,淋了半条街'; function tit($file){ //读文件 $h = file_get_contents($file); $h = str_re ...
- 【转】VC 线程间通信的三种方式
原文网址:http://my.oschina.net/laopiao/blog/94728 1.使用全局变量(窗体不适用) 实现线程间通信的方法有很多,常用的主要是通过全局变量.自定义消息和 ...
- 【转】VC 模式对话框和非模式对话框的创建,销毁和区别
原文网址:http://blog.csdn.net/mycaibo/article/details/6002151 VC 模式对话框和非模式对话框的创建,销毁和区别 在WIN32中,模式对话框的创 ...
- SAE部署Django1.6+MySQL
[解决]SAE部署Django1.6+MySQL 终于可以舒口气了,今天大部分时间都在搞这个,很是蛋疼,网上资料良莠不齐,我不信这个之前没人做过,但是他们确实分享的不够好. 废话不多说,还是记录一下今 ...
- 局域网使用NAT进行测试第三方接口
问题分析 在局域网内开发一些涉及到第三方的接口调用功能时(譬如:支付),需要对方服务器进行接口回调,接受对方发送过来的信息.问题来了,我们一般开发都是在内网,如何才能获取到外网返回的数据呢?如 ...
- 枚举生成1~n的排序
/*枚举生成1~n的排列*/ #include <iostream> #include<algorithm> #include<queue> #include< ...
- 在线pubmed
ESearch(文本搜索) eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi http://eutils.ncbi.nlm.nih.gov/entr ...
- 脱壳系列(四) - eXPressor 壳
先用 PEiD 查一下壳 用 OD 载入程序 这里有一串字符串,是壳的名称和版本号 按 Alt+M 显示内存窗口 这里只有三个区段,后面两个是壳生成的,程序的代码段也包含在里面 利用堆栈平衡 按 F8 ...
- Julia - 数学运算
算术运算符 算术运算符适用于所有的基本数值类型 +x,一元加法,就是 x 本身 -x,一元减法,x 的相反数 x + y,二元加法,做加法运算 x - y,二元减法,做减法运算 x * y,乘法,做乘 ...
- jquery easy ui 的formatter 格式化函数代码