做法网上到处都有就不说了.

这题其实是之前做的….不过由于人太傻现在才想明白比较字典序进行贪心的正确性….

方便起见,在两个串的最右端都加上很大但不相同的字符,避免第lcp+1个字符不存在的边界。

如果两个串当前最左端的字符不相同显然选较小的.

否则,设两个剩下的串的lcp长度为x,那么两个串的第lcp+1个字符(此时必然都存在这个字符,因为我们之前在右端加了很大的哨兵)必然不同.不妨假设第一个串的第lcp+1个字符较小.

考虑先选第二个串的第1个字符的某种方案.

如果这种方案中,我们先选了第二个串的第lcp+1个字符再选第一个串的第lcp+1个字符,那么把这种方案在选择第二个串的第lcp+1个字符之前的所有操作中选第一个串的操作改为选第二个串,选第二个串的操作改为选第一个串,最后把选择第二个串的第lcp+1个字符改为选择第一个串的第lcp+1个字符,这样得到先选第一个串的第1个字符且字典序更小的方案.

如果这种方案中,我们先选了第一个串的第lcp+1个字符,那么第一次操作后第一个串选了0个字符,第二个串选了1个字符.选择第一个串的第lcp+1个字符后,第一个串选了lcp+1个字符,第二个串选了<=lcp个字符.一开始第二个串选的字符多,最后第一个串选的字符多,因为每次只能进行一个操作,中间必然存在某次操作,使得这次操作后两个串选择的字符数目相同.那么我们把这次操作和这次操作之前的操作都反转一下(即:原先这次操作选第一个串,反转后这次操作选第二个串,原先选第二个串,反转后选第一个串),之后的操作不变,就可以得到一个字典序相同但是先选第一个串的第1个字符的方案.

于是,对于任何一个先选字典序较大的串的方案,我们都可以找到一个至少不会更差的方案先选字典序较小的串.因此最优方案必然是每次选择字典序较小的串.

某奶牛题poj3623&bzoj1692是从一个字符串两侧拿出字符组成字符串要求字典序最小,同样可以分这样两种情况考虑,由选字典序大的一侧方案得到选字典序小的一侧的方案,且使得最终结果不会更差.两侧开始的串的lcp可能会有重叠部分,拿这个串可能会拿着拿着拿到另一端,但仍然可以进行操作的反转,因此还是可以这么证.

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=;
int sa[maxn],rank[maxn];
int tmp1[maxn],tmp2[maxn],key[maxn],sum[maxn];
int a[maxn];
void getsa(int n,int m){
int *rk=tmp1,*res=tmp2,i,j,p;
for(i=;i<m;++i)sum[i]=;
for(i=;i<n;++i)sum[rk[i]=a[i]]++;
for(i=;i<m;++i)sum[i]+=sum[i-];
for(i=n-;i>=;--i){
sa[--sum[rk[i]]]=i;
}
for(j=,p=;p<n;j<<=,m=p){
for(p=,i=n-j;i<n;++i)res[p++]=i;
for(i=;i<n;++i)if(sa[i]>=j)res[p++]=sa[i]-j;
for(i=;i<n;++i)key[i]=rk[res[i]];
for(i=;i<m;++i)sum[i]=;
for(i=;i<n;++i)sum[rk[i]]++;
for(i=;i<m;++i)sum[i]+=sum[i-];
for(i=n-;i>=;--i)sa[--sum[key[i]]]=res[i];
for(res[sa[]]=,p=,i=;i<n;++i){
if(sa[i]+j<n&&sa[i-]+j<n&&rk[sa[i]]==rk[sa[i-]]&&rk[sa[i]+j]==rk[sa[i-]+j])res[sa[i]]=p-;
else res[sa[i]]=p++;
}
swap(rk,res);
}
for(int i=;i<n;++i)rank[sa[i]]=i;
}
int main(){
int n,m;scanf("%d",&n);
for(int i=;i<n;++i)scanf("%d",&a[i]);
a[n]=;
scanf("%d",&m);
for(int i=;i<=m;++i)scanf("%d",&a[n+i]);
a[n+m+]=;
getsa(n+m+,); int pt1=,pt2=n+;
int lim=n+m;
for(int i=;i<=lim;++i){
printf("%d ",rank[pt1]>rank[pt2]?a[pt2++]:a[pt1++]);
}
return ;
}
#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;
const int maxn=;
int tmp[][maxn],sum[maxn],key[maxn],sa[maxn],rank[maxn];
char str[maxn];
void getsa(int n,int m){
int i,j,k,p,*rk=tmp[],*res=tmp[];
for(i=;i<m;++i)sum[i]=;
for(i=;i<n;++i)sum[rk[i]=str[i]]++;
for(i=;i<m;++i)sum[i]+=sum[i-];
for(i=n-;i>=;--i)sa[--sum[rk[i]]]=i;
for(j=,p=;p<n;m=p,j<<=){
for(i=;i<m;++i)sum[i]=;
for(p=,i=n-j;i<n;++i)res[p++]=i;
for(i=;i<n;++i)if(sa[i]>=j)res[p++]=sa[i]-j;
for(i=;i<n;++i)sum[key[i]=rk[res[i]]]++;
for(i=;i<m;++i)sum[i]+=sum[i-];
for(i=n-;i>=;--i)sa[--sum[key[i]]]=res[i];
for(res[sa[]]=,p=,i=;i<n;++i){
res[sa[i]]=(rk[sa[i]]==rk[sa[i-]]&&rk[sa[i]+j]==rk[sa[i-]+j])?p-:p++;
}
swap(res,rk);
}
for(i=;i<n;++i)rank[sa[i]]=i;
}
int main(){
int n;scanf("%d",&n);
for(int i=;i<n;++i){
while(str[i]=getchar(),!isgraph(str[i]));
}
for(int i=;i<n;++i)str[n+i+]=str[n-i-];
str[n]='Z'+;str[*n+]='Z'+;
getsa(*n+,);
int pt1=,pt2=n+;
int cnt=;
for(int i=;i<=n;++i){
if(rank[pt1]<rank[pt2]){
printf("%c",str[pt1]);pt1++;
}else{
printf("%c",str[pt2]);pt2++;
}
cnt++;
if(cnt%==)printf("\n");
}
return ;
}

bzoj4278[ONTAK2015]Tasowanie & bzoj1692[USACO 2007Dec]队列变换(Best Cow Line) 贪心正确性证明的更多相关文章

  1. bzoj1640[Usaco2007 Nov]Best Cow Line 队列变换*&&bzoj1692[Usaco2007 Dec]队列变换*

    bzoj1640[Usaco2007 Nov]Best Cow Line 队列变换 bzoj1692[Usaco2007 Dec]队列变换 题意: 有一个奶牛队列.每次可以在原来队列的首端或是尾端牵出 ...

  2. [bzoj1692][Usaco2007 Dec]队列变换_后缀数组_贪心

    队列变换 bzoj-1692 Usaco-2007 Dec 题目大意:给定一个长度为$n$的字符串.每次从头或尾取出一个字符加到另一个字符串里.要求变换后生成的字符串字典序最小,求字典序最小的字符串. ...

  3. 【BZOJ-1692&1640】队列变换 后缀数组 + 贪心

    1692: [Usaco2007 Dec]队列变换 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1153  Solved: 482[Submit][St ...

  4. BZOJ1692: [Usaco2007 Dec]队列变换

    1692: [Usaco2007 Dec]队列变换 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 594  Solved: 246[Submit][Sta ...

  5. [bzoj1692] [Usaco2007 Dec]队列变换 (hash||暴力)

    本题同bzoj1640...双倍经验双倍幸福 虽然数据范围n=3w然而O(n²)毫无压力= = http://blog.csdn.net/xueyifan1993/article/details/77 ...

  6. BZOJ4278 : [ONTAK2015]Tasowanie

    首先在串的末尾加上1000,然后进行归并,每次取字典序较小的那个后缀即可. 用hash+二分支持查询lcp,时间复杂度$O(n\log n)$. #include<cstdio> type ...

  7. [bzoj1692][Usaco2007 Dec]队列变换——贪心+后缀数组

    Brief Description 给定一个数列,您每次可以把数列的最前面的数或最后面的数移动到新数列的开头,使得新数列字典序最小.输出这个新序列. Algorithm Design 首先我们可以使用 ...

  8. BZOJ4278 [ONTAK2015]Tasowanie[后缀数组+贪心]

    题目 求两数组归并后的数组最小字典序排列. 嘛,可能本人在贪心这块还是太弱了(或者说什么都弱),如果不知道是字符串题估计也想不起来用sa. 显然看得出归并时字典序小的那个数组先往里面加,这就是要比较两 ...

  9. [BZOJ4278] [ONTAK2015]Tasowanie 贪心+后缀数组

    题目链接 最近做题目好像有点东一榔头西一棒.好吧其实订正模拟题的时候需要用到什么感觉不太熟的就写一下吧. 显然直接贪心,比较两个点后面的串的字典序,小就选谁就可以了. 可以把两个串接起来,加一个\(i ...

随机推荐

  1. BZOJ1003_物流运输_KEY

    题目传送门 这是一道DP+最短路径的好题. 首先预处理每天每个点的最短路径. 用SPFA进行处理.即cost[i][j]为第i天到底j天的1到M点的最小花费. 就可以水水的DP. 设f[i]为第i天的 ...

  2. 北京Uber优步司机奖励政策(2月23日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  3. android学习十三 首选项

    1,首选项可用用来持久保存用户设置,游戏最高分等 2,首选项有,列表首选项,复选框首选项,对话框首选项.. 3,通过xml文件和代码创建首选项      addPreferencesFromResou ...

  4. excell 导入 导出

    1.jar包 2.POIUtils工具类 package com.esstglobal.service.utils; import java.io.BufferedInputStream; impor ...

  5. js 中常用到的封装方法

    /** * 获取URL参数 */ function getQueryString(name) { var reg = new RegExp("(^|&)" + name + ...

  6. 【SpringCloud】第三篇: 服务消费者(Feign)

    前言: 必需学会SpringBoot基础知识 简介: spring cloud 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理.服务发现.断路器.路由.微代理.事件总线.全局锁.决策竞选. ...

  7. tpo-08 C1 submit a document for graduation

    第 1 段 1.Listen to a conversation between a student and a registrar. 请听一段学生和老师的对话. 第 2 段 1.Hi, I'd li ...

  8. 关于java中“使用了未经检查或不安全的操作、有关详细信息,请使用 ——Xlint:unchecked重新编译”

    今天看<算法 第4版>排序章节时,发现了一个了一个小问题.先贴一下代码: public class Selection{ public static void main(String[]a ...

  9. bson文件的切分

    描述 最近遇到问题需要将较大的bson文件(MongoDB导出的二进制json文件)按文档(记录)进行切分,网上这方面的资料实在太少,弄了一天多终于达到了基本要求(还不知道有没有BUG) 代码 pac ...

  10. 【转载】android 常用开源框架

    对于Android初学者以及对于我们菜鸟,这些大神们开发的轻量级框架非常有用(更别说开源的了). 下面转载这10个框架的介绍:(按顺序来吧没有什么排名). 一.  Afinal 官方介绍: Afina ...