题意:

给出一个字符串,至多将其划分为n部分,每一部分取出字典序最大的子串ci,最小化 最大的ci

先看一个简化版的问题:

给一个串s,再给一个s的子串t,问能否通过将串划分为k个部分,使t成为划分后的s的字典序最大子串

对于这个问题,从串s的最后面开始,一个字符一个字符的向前推
如果当前[l,r]字典序比t大,那么[l+1,r]就要单独成为一段
比较子串字典序大小用二分+哈希
因为我们是一个字符一个字符的向前推的,所以一定是新的l使当前[l,r]字典序比t大
所以如果此时l==r,那么这个t不可能成为字典序最大子串
如果最后部分数<=k,则这个t可以
那么本题只需要二分子串t就好了
所以现在问题变成如何获取字典序 排名第k的子串
这个可以通过后缀数组的height求出
[sa[1],sa[1]] 是字典序第1小
[sa[1],sa[1]+1]是字典序第2小
……
[sa[1],n]是字典序第n-sa[1]+1 小
[sa[2],sa[2]+height[2]] 是下一个
再下一个是 [sa[2],sa[2]+height[2]+1]
…… 
#include<cstdio>
#include<cstring>
#include<algorithm>using namespace std;#define N 100001typedef long long LL;const int base=13331;int n,m;
char s[N];int a[N];
int v[N];
int p,q=1,k;
int sa[2][N],rk[2][N];
int h[N];unsigned long long Pow[N],has[N];pair<int,int>interval[N];void mul(int *sa,int*rk,int *SA,int *RK)
{for(int i=1;i<=n;++i) v[rk[sa[i]]]=i;for(int i=n;i;--i) if(sa[i]>k) SA[v[rk[sa[i]-k]]--]=sa[i]-k;for(int i=n-k+1;i<=n;++i) SA[v[rk[i]]--]=i;for(int i=1;i<=n;++i) RK[SA[i]]=RK[SA[i-1]]+(rk[SA[i]]!=rk[SA[i-1]] || rk[SA[i]+k]!=rk[SA[i-1]+k]);
}void presa()
{for(int i=1;i<=n;++i) v[a[i]]++;for(int i=1;i<=26;++i) v[i]+=v[i-1];for(int i=1;i<=n;++i) sa[p][v[a[i]]--]=i;for(int i=1;i<=n;++i) rk[p][sa[p][i]]=rk[p][sa[p][i-1]]+(a[sa[p][i]]!=a[sa[p][i-1]]);for(k=1;k<n;k<<=1,swap(p,q)) mul(sa[p],rk[p],sa[q],rk[q]);
}void get_height()
{int j,k=0;for(int i=1;i<=n;++i){j=sa[p][rk[p][i]-1];while(a[i+k]==a[j+k]) k++;h[rk[p][i]]=k;if(k) k--;}
}void prehash()
{Pow[0]=1;for(int i=1;i<=n;++i) Pow[i]=Pow[i-1]*base;for(int i=1;i<=n;++i) has[i]=has[i-1]*base+a[i];
}pair<int,int>select(LL k)
{int now;LL sum=0;int l,r;for(int i=1;i<=n;++i) {now=interval[i].second-interval[i].first+1;if(sum+now>=k){l=sa[p][i];r=interval[i].first+k-sum-1;return make_pair(l,r);}sum+=now;}
}unsigned long long get_hash(int l,int r)
{return has[r]-has[l-1]*Pow[r-l+1];
}int cmp(pair<int,int>x,pair<int,int>y)
{if(get_hash(x.first,x.second)==get_hash(y.first,y.second)) return 0;int Lx=x.second-x.first+1,Ly=y.second-y.first+1;int l=1,r=min(Lx,Ly),mid,tmp=0;while(l<=r){mid=l+r>>1;if(get_hash(x.first,x.first+mid-1)==get_hash(y.first,y.first+mid-1)) tmp=mid,l=mid+1;else r=mid-1;}if(tmp<min(Lx,Ly)) return s[x.first+tmp]<s[y.first+tmp] ? -1 : 1;return Lx<Ly ? -1 : 1;
}bool check(pair<int,int>now)
{int l=n,r=n,sum=1;while(l>=1)if(cmp(make_pair(l,r),now)==1) {if(l==r) return false;r=l;sum++;if(sum>m) return false;}else l--;return true;
}void solve()
{LL l=1,r=0;for(int i=1;i<=n;++i){interval[i].first=sa[p][i]+h[i];interval[i].second=n;r+=interval[i].second-interval[i].first+1;}LL mid,tmp;pair<int,int>now;while(l<=r){mid=l+r>>1;now=select(mid);if(check(now)) tmp=mid,r=mid-1;else l=mid+1;}now=select(tmp);l=now.first; r=now.second;for(int i=l;i<=r;++i) putchar(s[i]);
}    int main()
{freopen("string.in","r",stdin);freopen("string.out","w",stdout);scanf("%d",&m);scanf("%s",s+1);n=strlen(s+1);for(int i=1;i<=n;++i) a[i]=s[i]-'a'+1;presa();get_height();prehash();solve();
}

转载于:https://www.cnblogs.com/TheRoadToTheGold/p/8981700.html

2016vijos 1-1 兔子的字符串(后缀数组 + 二分 + 哈希)相关推荐

  1. 洛谷 P4094 [HEOI2016/TJOI2016]字符串 后缀数组+二分+主席树

    题目链接 后缀数组 题目分析: sa[i] – 第i小的后缀的编号 rank[i] --编号为i的后缀排第几: height[i] – 第i和第i-1的最长lcp最长公共前缀: 1.二分答案,答案肯定 ...

  2. BZOJ3473:字符串(后缀数组,主席树,二分,ST表)

    Description 给定n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串? Input 第一行两个整数n,k. 接下来n行每行一个字符串. Output 一 ...

  3. HDU - 5030 Rabbit's String(后缀数组+二分)

    题目链接:点击查看 题目大意:给出一个字符串,现在要求将其分为不大于k个连续的子串,对于每个子串求出字典序最大的子串,现在要求所有子串的最大子串的最大值最小,输出这个最大子串 题目分析:最大值最小,标 ...

  4. HDU - 5008 Boring String Problem(后缀数组+二分)

    题目链接:点击查看 题目大意:给出一个字符串,接下来给出 q 个询问,每次询问字符串中第 k 大的子串,要求输出该字串的左右端点,如果有多个答案,输出左端点最小的一个 题目分析:因为在求出后缀数组后, ...

  5. SPOJ 220 Relevant Phrases of Annihilation(后缀数组+二分答案)

    [题目链接] http://www.spoj.pl/problems/PHRASES/ [题目大意] 求在每个字符串中出现至少两次的最长的子串 [题解] 注意到这么几个关键点:最长,至少两次,每个字符 ...

  6. [BZOJ4310]跳蚤-后缀数组-二分答案

    跳蚤 Description 很久很久以前,森林里住着一群跳蚤.一天,跳蚤国王得到了一个神秘的字符串,它想进行研究.首先,他会把串分成不超过 k 个子串,然后对于每个子串 S,他会从S的所有子串中选择 ...

  7. bzoj3277 串 (后缀数组+二分答案+ST表)

    常见操作:先把所有串都连到一起,但中间加上一个特殊的符号(不能在原串中/出现过)作为分割 由于全部的子串就等于所有后缀的所有前缀,那我们对于每一个后缀,去求一个最长的前缀,来满足这个前缀在至少K个原串 ...

  8. UVa 11107 (后缀数组 二分) Life Forms

    利用height值对后缀进行分组的方法很常用,好吧,那就先记下了. 题意: 给出n个字符串,求一个长度最大的字符串使得它在超过一半的字符串中出现. 多解的话,按字典序输出全部解. 分析: 在所有输入的 ...

  9. POJ - 1226 Substrings(后缀数组+二分)

    题目链接:点击查看 题目大意:给出n个字符串,求出n个字符串中或者在他们翻转过来的字符串中,出现的最长公共子串的长度 题目分析:说白了就是求n个字符串中的最长公共子串的变形,而这个变形也相当简单,只是 ...

  10. SPOJ - PHRASES Relevant Phrases of Annihilation(后缀数组+二分)

    题目链接:点击查看 题目大意:给出n个字符串,求出n个字符串中的最长公共子串,且该字串必须在每个字符串中出现至少两次且不能重叠 题目分析:结合之前整理过的: 一个字符串中两个不重叠的重复子串的最大长度 ...

最新文章

  1. python后端开发框架加密_Flask框架实现的前端RSA加密与后端Python解密功能详解
  2. 苹果如何使用神经网络在点云中做对象检测
  3. HDOJ 1070 排序 水
  4. html缩进快捷键_Mac技巧|如何高效使用苹果便笺?利用便笺快捷键快速完成操作!
  5. python函数修饰器_Python函数装饰器指南
  6. c++11-template template Parameter
  7. jmx JVM监测工具使用
  8. 将undefault和null的数据转换成bool类型的数据 使用!!
  9. 前端学习(2547):数据代理
  10. 在pycharm中自定义模板代码,快速输出固定代码块
  11. 教育部统考 计算机应用,9月教育部统考《计算机应用基础》试卷一附答案.doc
  12. 网络应用瑞士军刀——Zentyal(4 常用命令)
  13. Python selenium 实现大麦网自动购票过程
  14. 任务栏右键工具栏里的语言栏没有的修复.reg
  15. Selenium中的鼠标单击事件
  16. MPFC++ wrapper by Pavel
  17. SOUI总结之盒子模型
  18. c# socket套接字
  19. b2b2c微信小程序商城源码
  20. 算法——钻石金字塔(动态规划+贪心)

热门文章

  1. 小米手机上的云便签怎么把数据导出电脑
  2. 前端js 下载zip文件并解压
  3. Kali Linux 暴力破解 wifi密码
  4. 竞拍系统c语言,C++版扫拍卖源代码,非程序,这下不会删帖吧?
  5. VS2019 windows桌面应用_基于.Net Core平台开发WinForms应用程序
  6. 北京科技大学计算机实践,北京科技大学计算机实践报告-Excel 练习
  7. 如何添加CDN加速域名
  8. 获取当前时间戳的方法
  9. CATIA怎么约束快捷键_CATIA超实用快捷键和技巧
  10. 图像分割与GAN网络