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

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

方便起见,在两个串的最右端都加上很大但不相同的字符,避免第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. SpaceVim 语言模块 python

    原文连接: https://spacevim.org/cn/layers/lang/python/ 模块简介 功能特性 依赖安装及启用模块 启用模块 语法检查 代码格式化 格式化 imports 快捷 ...

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

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

  3. 上海Uber优步司机奖励政策(1月4日~1月10日)

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

  4. P2664 树上游戏

    P2664 树上游戏 https://www.luogu.org/problemnew/show/P2664 分析: 点分治. 首先关于答案的统计转化成计算每个颜色的贡献. 1.计算从根出发的路径的答 ...

  5. Java:当前线程运行完毕,再运行后续逻辑

    一.问题描述 在程序设计中,可能存在这样的情景:主线程中存在一个子线程,子线程需要在执行完毕后为后续代码逻辑提供参数.但在代码执行时,子进程还没执行完毕,后续的代码已经开始执行了,这时候就会出现参数为 ...

  6. 阅读笔记《JavaScript语言精粹》

    阅读笔记<JavaScript语言精粹> 对象 1.检索属性 使用[]和. 2.引用传递 JavaScript的简单数据类型包括数字.字符串.布尔值.null值和undefined值.其它 ...

  7. 获取项目中.txt 文件的内容

    package com.fh.controller.ruitai.util; import java.io.BufferedInputStream; import java.io.File; impo ...

  8. 微信小程序如何性能测试?

    背景: 微信小程序作为手机页面的一种,相比传统的网站和应用来说存在比较特殊的地方: 1.  开发者往往对程序做了限制,只能通过微信客户端访问 2.  通过微信的Oauth进行认证 这样往往会导致我们的 ...

  9. 适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域

    此篇文章是对上一篇文章(http://www.ifiero.com/index.php/archives/611)的进一步补充,主要说明如何适配Apple的最新三款手机iPhoneXs.iPhoneX ...

  10. Python2快速入门教程,只需要这十五张图片就够了!

    今天给大家分享的教程是适用于Python 2.7,但它可能适用于Python 2.Python 2.7将停止在2020中的支持. 与Python 2.7和3兼容的Python代码是完全可能的.通过使用 ...