hdoj 5008 后缀数组+RMQ+二分
2015-08-05 19:52
253 查看
hdoj 5008
题意:给一个字符串,问该字符串第k个子串的最小字典序位置。其中k需要L xor R xor V求得,L R是前一个询问的子串位置,说白了就是强制在线。
思路:先是后缀数组处理出子串去重后的编号,然后先求得第k个子串的位置i,因为这个位置只是后缀数组中第一次出现的位置,而不一定是原字符串中最左边的位置,所以真正的位置可能在i的左边或右边。如果遍历去找肯定会超时,但是我们可以二分。
假设第k个子串的长度为d,起始下标为i,真实下标为j,那么i到j的公共前缀一定大于等于d,即height[i] >= d, height[i + 1] >=d ....height[j]>=d(j也可能在i的左边),那么我们只要保证min(height[i],height[i + 1]...height[j])>= d就可以了,这就用到了RMQ,我这里是用线段树实现的。
这样就可以求出含有公共前缀的最大范围L和R(L<=i, R>=i)。
然后只要求出[L,R]中最小的sa就可以了,这里就用到了另一个RMQ。
所以我们一共需要两个RMQ,一个用来查询区间最小公共前缀长度(height),一个用来查询区间最小后缀起始位置(sa)。
另外还有一些细节,后缀数组得到的答案L,R是以0开始的,xor和输出答案的时候记得加1(我就这里wa了好多次。。。TAT),还有k、v是long long。
题意:给一个字符串,问该字符串第k个子串的最小字典序位置。其中k需要L xor R xor V求得,L R是前一个询问的子串位置,说白了就是强制在线。
思路:先是后缀数组处理出子串去重后的编号,然后先求得第k个子串的位置i,因为这个位置只是后缀数组中第一次出现的位置,而不一定是原字符串中最左边的位置,所以真正的位置可能在i的左边或右边。如果遍历去找肯定会超时,但是我们可以二分。
假设第k个子串的长度为d,起始下标为i,真实下标为j,那么i到j的公共前缀一定大于等于d,即height[i] >= d, height[i + 1] >=d ....height[j]>=d(j也可能在i的左边),那么我们只要保证min(height[i],height[i + 1]...height[j])>= d就可以了,这就用到了RMQ,我这里是用线段树实现的。
这样就可以求出含有公共前缀的最大范围L和R(L<=i, R>=i)。
然后只要求出[L,R]中最小的sa就可以了,这里就用到了另一个RMQ。
所以我们一共需要两个RMQ,一个用来查询区间最小公共前缀长度(height),一个用来查询区间最小后缀起始位置(sa)。
另外还有一些细节,后缀数组得到的答案L,R是以0开始的,xor和输出答案的时候记得加1(我就这里wa了好多次。。。TAT),还有k、v是long long。
#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], len; struct Tree{ int l, r, val[2]; }tree[MAXN * 4];//0 height 1 sa int cnt = 0; void buildtree(int rt, int l, int r) { tree[rt].l = l, tree[rt].r = r; if(l == r) { tree[rt].val[0] = height[cnt]; tree[rt].val[1] = sa[cnt++]; return ; } int mid = (l + r) / 2; buildtree(rt * 2, l, mid); buildtree(rt * 2 + 1, mid + 1, r); tree[rt].val[0] = min(tree[rt * 2].val[0], tree[rt * 2 + 1].val[0]); tree[rt].val[1] = min(tree[rt * 2].val[1], tree[rt * 2 + 1].val[1]); } int query(int rt, int l, int r, int id) { if(tree[rt].l == l && tree[rt].r == r) { return tree[rt].val[id]; } int mid = (tree[rt].l + tree[rt].r) / 2; if(l > mid) return query(rt * 2 + 1, l, r, id); else if(r <= mid) return query(rt * 2, l, r, id); else { return min(query(rt * 2, l, mid, id), query(rt * 2 + 1, mid + 1, r, id)); } } int bsl(int x, int LEN) { int l = 1, r = x - 1, mid, ret = x; while(l <= r) { mid = (l + r) >> 1; if(query(1, mid + 1, x, 0) >= LEN) { ret = mid, r = mid - 1; } else l = mid + 1; } return ret; } int bsr(int x, int LEN) { int l = x + 1, r = len, mid, ret = x; while(l <= r) { mid = (l + r) >> 1; if(query(1, x + 1, mid, 0) >= LEN) { ret = mid, l = mid + 1; } else r = mid - 1; } return ret; } main() { int n; while(~scanf("%s", str)) { len = strlen(str); suffix_array(str); memset(tree, 0, sizeof tree); cnt = 1; buildtree(1, 1, len); for(int i = 1; i <= len; i++) { sum[i] = sum[i - 1] + len - sa[i] - height[i]; // printf("sa[%d] = %d height[%d] = %d\n", i, sa[i], i, height[i]); } long long l = 0, r = 0, v, k; scanf("%d", &n); while(n--) { scanf("%I64d", &v); v ^= l ^ r; k = v + 1; // printf("k = %I64d\n", k); if(k > sum[len]) { puts("0 0"); l = r = 0; continue; } int t = lower_bound(sum + 1, sum + 1 + len, k) - sum; l = sa[t], r = len - (sum[t] - k + 1); int d = r - l + 1; int L = bsl(t, d), R = bsr(t, d); // printf("L = %d R = %d\n", L, R); int ans = query(1, L, R, 1); // printf("l = %d\n", ans); l = ans + 1, r = l + d - 1; printf("%I64d %I64d\n", l, r); } } }
相关文章推荐
- Eclipse下使用maven创建多模块项目
- 创客 机械臂坦克电机不转问题
- SVM 多分类 -SVM分类opencv3.0源代码
- Android ListView中点击单行实现RadioButton的单选功能,自定义Item布局文件
- 深入分析android5.1 healthd
- 关于Orcale中not in与空值在查询上的问题
- linux下安装mongodb
- 网络应用发布到linux上的web服务器上页面上显示麻将牌式字符的问题
- ANDROID中去掉ACTIONBAR或TABWIDGET的分隔线
- JDK安装
- 08-05 Server、Client可连续发送 生产者、消费者 死锁解决 Server、Client窗口实现
- 如何用create table ... as select ...建与另一个实例中的用户下表结构相同的表
- POJ 2242 The Circumference of the Circle 简单数学
- android timessquare日期控件
- *(volatile unsigned long *) 语法
- C#高级编程小结
- 《集体失忆的黑暗时代》:已故加拿大公共知识分子关于城市规划与人类文明的随笔,三星推荐
- 最长公共子序列 nyoj 36
- RF随机森林
- cin与scanf的区别