【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”, ...
随机推荐
- spring容器启动
1 主要类 ContextLoaderListener:注册在web.xml中,web应用启动时,会创建它,并回调它的initWebApplicationContext()方法,从而创建并启动spri ...
- linux查找目录下的所有文件中是否含有某个字符串 (转)
查找目录下的所有文件中是否含有某个字符串 find .|xargs grep -ri "IBM" 查找目录下的所有文件中是否含有某个字符串,并且只打印出文件名 find .|xar ...
- 树的遍历——pat1043
http://pat.zju.edu.cn/contests/pat-a-practise/1043 给予N个数字组成二叉搜索树,判断这个数列是否由先序遍历得出或是镜像先序遍历得出,若是则输出相应的后 ...
- 安卓笔记-adb指令、打包安装
adb install -r -s xxx.apk -r重装 -s 安装到sd卡 安装软件 adb uninstall -k 包名 -k 只卸载程序不清除数据 adb ...
- 使用Apache POI操作Excel文件---在已有的Excel文件中插入一行新的数据
package org.test; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundEx ...
- GOF23设计模式之责任链模式(chain of responsibility)
一.责任链模式概述 将能够处理同一类请求的对象连成一条链,所提交的请求沿着链传递,链上的对象逐个判断是否有能力处理该请求. 如果能则处理,否则传递给链上的下一个对象去处理. 定义责任链 (1)通过链表 ...
- 初学者手册-IDEA中的Git
1.Git的更新.提交.还原 IDEA中Git的更新.提交.还原方法 2.设置Git的提交方式为http 3.
- 通过TortoiseGit来使用Github或Visual Studio Online版本控制管理
一.前言 关于Git源码不再多阐述,它就是一款开源分布式版本控制工具,它在源码管理领土上目前为止,使用者比例很大,越来越多的人使用该工具来管理项目源码,且相当多的开源的项目都移步到Github中,如: ...
- ODPS基础
遇到一个项目需求是统计128张分库分表的数据表记录的最大id,通过单表查询计算非常费时,也无法应对分表数更多的情况,因此考虑到通过odps进行任务发布和运算 在云端 http://d2.alibaba ...
- cobalt strike 第一节连接到团队的服务器
介绍:Cobalt Strike 一款以metasploit为基础的GUI的框架式渗透工具,集成了端口转发.服务扫描,自动化溢出,多模式端口监听,win exe木马生成,win dll木马生成,jav ...