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) ...
随机推荐
- pip使用简要说明
一.pip常用命令 安装指定包 pip install SomePackage #最新版本 安装指定包 pip install SomePackage==1.0.4 #指定版本 安装指定包 pip i ...
- redux 数据规律
counter increase info todos 为 reducers 文件名 export default combineReducers({ todos, visibilityFilter ...
- Linux基础命令---显示树形进程pstree
pstree pstree显示正在运行的进程的树形结构,树以PID为根:如果省略了pid则以init为根.如果指定了用户名,则显示根植于该用户拥有的进程的所有进程树.如果pstree被调用为pstre ...
- ssh无秘钥登录
服务器1:172.16.20.136 服务器2:172.16.20.134 在服务器2上输入:ssh-keygen -t rsa (一路回车) scp /root/.ssh/id_ ...
- c--socket通信TCP篇
https://www.cnblogs.com/ashen/p/4474360.html #include <sys/socket.h> 2 #include <stdlib.h&g ...
- Linux的简单介绍.
Linux操作系统概述: Linux是基于Unix的开源免费的操作系统,由于系统的稳定性和安全性几乎成为程序代码运行的最佳系统环境.Linux是由Linux Torvalds(林纳斯·托瓦兹)起初开发 ...
- NATS—基础介绍
1. 介绍 NATS(Message bus): 从CloudFoundry的总架构图看,位于各模块中心位置的是一个叫nats的组件.NATS是由CloudFoundry的架构师Derek开发的一个开 ...
- SSL及其加密通信过程
SSL及其加密通信过程 什么是SSL SSL英文全称Secure Socket Layer,安全套接层,是一种为网络通信提供安全以及数据完整性的安全协议,它在传输层对网络进行加密.它主要是分为两层: ...
- 模拟实现ATM+购物商城程序
流程图: 需求: ATM:模拟实现一个ATM + 购物商城程序额度 15000或自定义实现购物商城,买东西加入 购物车,调用信用卡接口结账可以提现,手续费5%支持多账户登录支持账户间转账记录每月日常消 ...
- 快速阅读《QT5.9 c++开发指南》1
简介:<QT5.9 c++开发指南>的作者是和i三位主要从事地球物理探测仪器设计.数据处理方法研究和软件开发等工作的博士们,这本书以QT Widget为主要内容,比较全面地教授了QT开发桌 ...