hdoj 5030 后缀数组+二分
来源:互联网 发布:爱思for mac官方下载 编辑:程序博客网 时间:2024/05/29 16:16
hdoj 5030
题意:给一个字符串,最多分割k次,求分割后最大的子串。
思路:
首先通过len - sa[i] - height[i]对子串去重(len是字符串长度),然后枚举最大的子串,看这个子串能否在分割k次以内实现。
第k个子串:
t = lower_bound(sum + 1, sum + 1 + len, k) - sum;// 即子串所属后缀的起始位置, sum[i]是到字符串第i位有多少不同的子串。
L = sa[t], R = len - (sum[t] - k + 1);
关键在于如何分割:因为每个子串都是某个后缀的前缀,所以要在sa[i]+length处分割(length是枚举的子串长度),做标记点(如果这个串是后缀就不用分割),然后遍历当前排名后面的后缀(这后面的后缀才有可能要被分割)做标记,最后在遍历标记找分割点,看是否满足条件,详见代码。
#include <cstdlib>#include <cstring>#include <cstdio>#include <algorithm>using namespace std;const int MAXN = 100010;int sa[MAXN];int t1[MAXN],t2[MAXN],c[MAXN];int Rank[MAXN],height[MAXN];void build_sa(int s[],int n,int m){ int i,j,p,*x=t1,*y=t2; for(i=0;i<m;i++)c[i]=0; for(i=0;i<n;i++)c[x[i]=s[i]]++; for(i=1;i<m;i++)c[i]+=c[i-1]; for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i; for(j=1;j<=n;j<<=1) { p=0; for(i=n-j;i<n;i++)y[p++]=i; for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j; for(i=0;i<m;i++)c[i]=0; for(i=0;i<n;i++)c[x[y[i]]]++; for(i=1;i<m;i++)c[i]+=c[i-1]; for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i]; swap(x,y); p=1;x[sa[0]]=0; for(i=1;i<n;i++) x[sa[i]]=y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++; if(p>=n)break; m=p; }}void getHeight(int s[],int n){ int i,j,k=0; for(i=0;i<=n;i++)Rank[sa[i]]=i; for(i=0;i<n;i++) { if(k)k--; j=sa[Rank[i]-1]; while(s[i+k]==s[j+k])k++; height[Rank[i]]=k; }}int seq[MAXN];void suffix_array(char str[]){ int len = strlen(str); for(int i = 0; i <= len; i++) seq[i] = str[i]; seq[len] = 0; build_sa(seq, len + 1, 128); getHeight(seq, len);}char str[MAXN];//==================================================================================long long sum[MAXN];int divide[MAXN], len;bool isOK(long long k, int n) { int t = lower_bound(sum + 1, sum + 1 + len, k) - sum; int l = sa[t], r = len - (sum[t] - k + 1); int LEN = r - l + 1; for(int i = 0; i <= len; i++) divide[i] = -1; if(r + 1 < len) divide[sa[t]] = LEN; for(int i = t + 1; i <= len; i++) {//做标记 LEN = min(LEN, height[i]); if(height[i] == 0) return false; if(sa[i] + LEN < len) divide[sa[i]] = LEN; } int cnt = 0; r = len + 1; for(int i = 0; i < len; i++) {//遍历标记找分割点 if(i == r) { cnt++, r = len + 1; if(cnt >= n) return false; } if(divide[i] != -1) r = min(r, i + divide[i]); } return cnt < n;}main() { int n; while(~scanf("%d", &n) && n) { scanf("%s", str); suffix_array(str); len = strlen(str); for(int i = 1; i <= len; i++) { sum[i] = sum[i - 1] + len - sa[i] - height[i]; } long long l = 1, r = sum[len], ans; while(l <= r) { long long mid = (l + r) / 2; if(isOK(mid, n)) ans = mid, r = mid - 1; else l = mid + 1; } int t = lower_bound(sum + 1, sum + 1 + len, ans) - sum; l = sa[t], r = len - (sum[t] - ans + 1); for(int i = l; i <= r; i++) putchar(str[i]); putchar('\n'); }}
0 0
- hdoj 5030 后缀数组+二分
- hdoj 5008 后缀数组+RMQ+二分
- 【后缀数组】 HDOJ 5030 Rabbit's String
- poj3294 二分+后缀数组
- poj1743(后缀数组+二分)
- poj3261(后缀数组+二分)
- 后缀数组+二分poj3261
- bzoj4310【后缀数组+二分】
- 后缀数组LCP + 二分
- poj1745(后缀数组+二分)
- hihoCoder1403(后缀数组 + 二分)
- poj1743后缀数组+二分
- poj3261Milk Patterns 二分+后缀数组
- pku1743Musical Theme 后缀数组+二分
- POJ 3294 后缀数组+二分
- poj1743(后缀数组+二分)
- poj3261(后缀数组+二分)
- POJ 3261 后缀数组+二分
- 【讨论贴】关于父实子虚的疑问???
- Num 20 : HDOJ: 题目1402 : A*B ( 大数问题 )
- c++常量函数的理解
- myeclipse 10 的破解方法
- 电脑开机黑屏只出现鼠标光标
- hdoj 5030 后缀数组+二分
- 函数传值
- 冒泡算法
- 精通Hibernate——Java对象在JVM中的生命周期
- POJ 1005 I Think I Need a Houseboat 水
- Android Fragment 真正的完全解析(下)
- 选择排序
- CentOS 7 grub Linux 修改默认的启动操作系统
- 关于 self 和 super 在oc 中 的疑惑 与 分析