HDU 5442 Favorite Donut(暴力 or 后缀数组 or 最大表示法)
http://acm.hdu.edu.cn/showproblem.php?pid=5442
题意:
给出一串字符串,它是循环的,现在要选定一个起点,使得该字符串字典序最大(顺时针和逆时针均可),如果有多个字典序相同的,则输出下标最小的,如果下标也是相同的,则输出顺时针方向的。
思路:
用了三种方法:
①最简单的,暴力枚举所有情况,需要优化一下,先处理出字符串中字典序最大的单词,然后接下来的起点肯定是这个单词,否则就可以跳过这个起点。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std; int n,mx;
string s, best; int main()
{
// freopen("in.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
cin>>s;
mx = -;
for(int i=;i<n;i++)
if(s[i]-'a' > mx) mx = s[i]-'a';
s += s; int pos, dir;
best = "";
for(int i=;i<n;i++)
{
if(s[i]-'a'!=mx) continue;
string tmp = s.substr(i,n);
if(tmp > best)
{
pos = i;
dir = ;
best = tmp;
}
}
reverse(s.begin(),s.end());
for(int i=n-;i>=;i--)
{
if(s[i]-'a'!=mx) continue;
string tmp = s.substr(i,n);
if(tmp > best)
{
pos = n--i;
dir = ;
best = tmp;
}
else if(tmp == best)
{
if(n--i<pos)
{
pos = n--i;
dir = ;
}
}
}
printf("%d %d\n",pos+,dir);
}
return ;
}
暴力枚举
②后缀数组:
正序复制一遍,求一遍后缀数组,那么sa数组中排名最后的肯定是字典序最大的,此时直接取该值即可。而且此时它一定是最小坐标。
但是求逆序的时候需要注意一下,此时就不能求最小坐标了,因为逆序了,所以最小坐标在原来的字符串中是最大的,所以此时就要求最大字典序情况下的最小坐标。那么此时就要利用height数组了,从底向上依次遍历sa数组,如果和上一个的公共前缀是>=n的话,此时字典序是相同的,但是上一个字符串的坐标更大。知道height值<n。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=+; int n;
char s[maxn];
char s1[maxn],s2[maxn];
int sa[maxn],t[maxn],t2[maxn],c[maxn];
int Rank[maxn],height[maxn]; void build_sa(int m)
{
int *x=t,*y=t2;
//基数排序
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=;
//直接利用sa数组排序第二关键字
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];
//根据sa和y计算新的x数组
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 n)
{
int i,j,k=;
for(i=;i<=n;i++) Rank[sa[i]]=i;
for(i=;i<n;i++)
{
if(k) k--;
int j=sa[Rank[i]-];
while(s[i+k]==s[j+k]) k++;
height[Rank[i]]=k;
}
} int main()
{
//freopen("in.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
scanf("%s",s);
for(int i=;i<n;i++) s[i+n] = s[i];
s[*n] = '';
int tmp = n;
n = *n+;
build_sa();
getHeight(n-);
n = tmp; int pos1;
memset(s1,,sizeof s1);
for(int i=*n;i>=;i--)
{
if(sa[i]<n)
{
pos1 = sa[i];
break;
}
}
strncpy(s1,s+pos1,n); reverse(s,s+*n);
tmp = n;
n = *n+;
build_sa();
getHeight(n-);
n = tmp; int pos2;
memset(s2,,sizeof s2);
for(int i=*n;i>=;i--)
{
if(sa[i]<n)
{
while(height[i]>n) i--;
pos2 = n--sa[i];
break;
}
}
strncpy(s2,s+n--pos2,n); if(strcmp(s1,s2)>) printf("%d 0\n",pos1+);
else if(strcmp(s1,s2)<) printf("%d 1\n",pos2+);
else
{
if(pos1<=pos2) printf("%d 0\n",pos1+);
else printf("%d 1\n",pos2+);
}
}
return ;
}
后缀数组
③最大表示法:
参考论文:https://wenku.baidu.com/view/c6c5e7335a8102d276a22fa6.html
算法的具体思路:
(1)开始时,将字符串复制两份,设置两个指针,i=0,j=1.
(2)k=0,然后反复迭代直到s[i+k]!=s[j+k]
(3)如果k=n,那么返回较小的值,否则看情况滑动指针
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = +; int n;
char s[maxn];
char s1[maxn],s2[maxn]; int solve1(char *x) //最大表示法坐标最小
{
int i = , j = , k = ;
while(i<n && j<n && k<n)
{
int t = x[i+k] - x[j+k];
if(!t) k++;
else
{
if(t>) j+=k+;
else i+=k+;
if(i==j) j++;
k=;
}
}
return i<j?i:j;
} int solve2(char *x) //最大表示法且坐标最大
{
int i = , j = , k;
while(i < n && j < n)
{
while(x[i+k] == x[j+k] && k < n) k++;
if (k == n) //这个意思就是以i开头的和以j开头的字符串相同
{
int len = abs(i - j); //len的长度就是循环节
return n - len + i; //取坐标最大的
}
else
{
int t = x[i+k] - x[j+k];
if(t>) j+=k+;
else i+=k+;
if(i==j) j++;
k = ;
}
}
if(j >= n) return i;
return j;
} int main()
{
//freopen("in.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
scanf("%s",s);
for(int i=;i<n;i++) s[i+n] = s[i];
s[*n] = '\0'; int pos1 = solve1(s);
memset(s1,,sizeof s1);
strncpy(s1,s+pos1,n); reverse(s,s+*n);
int pos2 = solve2(s);
memset(s2,,sizeof s2);
strncpy(s2,s+pos2,n); int t = strcmp(s1,s2);
if(t>) printf("%d 0\n",pos1+);
else if(t<) printf("%d 1\n",n-pos2);
else
{
if(pos1+<=n-pos2) printf("%d 0\n",pos1+);
else printf("%d 1\n",n-pos2);
}
}
return ;
}
最大表示法
HDU 5442 Favorite Donut(暴力 or 后缀数组 or 最大表示法)的更多相关文章
- Hdu 5442 Favorite Donut (2015 ACM/ICPC Asia Regional Changchun Online 最大最小表示法 + KMP)
题目链接: Hdu 5442 Favorite Donut 题目描述: 给出一个文本串,找出顺时针或者逆时针循环旋转后,字典序最大的那个字符串,字典序最大的字符串如果有多个,就输出下标最小的那个,如果 ...
- hdu 5442 Favorite Donut 后缀数组
Favorite Donut Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid ...
- HDU 1403 Longest Common Substring(后缀数组,最长公共子串)
hdu题目 poj题目 参考了 罗穗骞的论文<后缀数组——处理字符串的有力工具> 题意:求两个序列的最长公共子串 思路:后缀数组经典题目之一(模版题) //后缀数组sa:将s的n个后缀从小 ...
- HDU 6194 string string string(后缀数组+RMQ)
string string string Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Oth ...
- HDU - 6223 Infinite Fraction Path (倍增+后缀数组)
题意:给定一个长度为n(n<=150000)的字符串,每个下标i与(i*i+1)%n连边,求从任意下标出发走n步能走出的字典序最大的字符串. 把下标看成结点,由于每个结点有唯一的后继,因此形成的 ...
- hdu5442(2015长春赛区网络赛1006)后缀数组+KMP /最小表示法?
题意:给定一个由小写字母组成的长度为 n 的字符串,首尾相连,可以从任意一个字符开始,顺时针或逆时针取这个串(长度为 n),求一个字典序最大的字符串的开始字符位置和顺时针或逆时针.如果有多个字典序最大 ...
- HDU 5442——Favorite Donut——————【最大表示法+kmp | 后缀数组】
Favorite Donut Time Limit: 1500/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) ...
- hdu 5030 Rabbit's String(后缀数组&二分法)
Rabbit's String Time Limit: 40000/20000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others ...
- HDU 5442 Favorite Donut
Favorite Donut Time Limit: 1500/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) ...
随机推荐
- sqlyog下载
sqlyog下载(附注册码):http://www.onlinedown.net/soft/24926.htm
- iOS项目之“返回”手势操作相关
在程序中,总会设置“返回”按钮,但不可能在每一个控制器中都去设置一次“返回”按钮,那如何设置全局的“返回”按钮呢? 首先自定义一个导航控制器,在tabBarController中添加子控制器时,使用这 ...
- 【开源】EasyFlash 新年发布 V4.0 beta 版,完全重写(转)
[开源]EasyFlash 新年发布 V4.0 beta 版,完全重写 EasyFlash V4.0 beta [开源]嵌入式闪存库 EasyFlash for STM32,支持Env和IAP
- Python-OpenCV基本操作cv2
1.图片加载.显示和保存 import cv2 # 生成图片 img = cv2.imread("1.jpg") # 生成灰色图片 imgGrey = cv2.imread(&qu ...
- php操作共享内存shmop类及简单使用测试(代码)
SimpleSHM 是一个较小的抽象层,用于使用 PHP 操作共享内存,支持以一种面向对象的方式轻松操作内存段.在编写使用共享内存进行存储的小型应用程序时,这个库可帮助创建非常简洁的代码.可以使用 3 ...
- 写给大忙人的Elasticsearch架构与概念(未完待续)
最新版本官方文档https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html文档增删改参考https://www ...
- python简说(二十九)线程,进程
进程: 一些资源的集合. 一个进程里面最少有一个线程,主线程.线程: 程序执行的最小单位. import threadingfrom threading import Threadimport tim ...
- topcoder srm 686 div1
problem1 link 左括号和右括号较少的一种不会大于20.假设左括号少.设$f[i][mask][k]$表示处理了前$i$个字符,其中留下的字符以$k$开头($k=0$表示'(',$k=1$表 ...
- 集训DAYn——组合数学(1)
组合 又到了我们信息老师讲数学课了,吼吼吼 然后数学老师中途探望了一下,哇塞塞,然后他看到黑板上的题,微妙的笑了. 排列: 从n个数中有序的选出m个数的方案数是多少?第一个数有n种取法,第二个数有n- ...
- grub基本应用
一.基本概念 GRUB(boot loader): GRand Unified Bootloader 两个版本: grub .x: grup legacy grub .x: grub2 grub ...