HDU5008 Boring String Problem(后缀数组 + 二分 + 线段树)
题目
Source
http://acm.hdu.edu.cn/showproblem.php?pid=5008
Description
In this problem, you are given a string s and q queries.
For each query, you should answer that when all distinct substrings of string s were sorted lexicographically, which one is the k-th smallest.
A substring si...j of the string s = a1a2 ...an(1 ≤ i ≤ j ≤ n) is the string aiai+1 ...aj. Two substrings sx...y and sz...w are cosidered to be distinct if sx...y ≠ Sz...w
Input
The input consists of multiple test cases.Please process till EOF.
Each test case begins with a line containing a string s(|s| ≤ 105) with only lowercase letters.
Next line contains a postive integer q(1 ≤ q ≤ 105), the number of questions.
q queries are given in the next q lines. Every line contains an integer v. You should calculate the k by k = (l⊕r⊕v)+1(l, r is the output of previous question, at the beginning of each case l = r = 0, 0 < k < 263, “⊕” denotes exclusive or)
Output
For each test case, output consists of q lines, the i-th line contains two integers l, r which is the answer to the i-th query. (The answer l,r satisfies that sl...r is the k-th smallest and if there are several l,r available, ouput l,r which with the smallest l. If there is no l,r satisfied, output “0 0”. Note that s1...n is the whole string)
Sample Input
aaa
4
0
2
3
5
Sample Output
1 1
1 3
1 2
0 0
分析
题目大概说给一个字符串,将所有不同子串从小到大排序,多次询问,每次询问输出第k个子串是哪个子串。
任何一个子串都是某个后缀的前缀,用后缀数组得到所有后缀的排列,然后对于各个后缀i能贡献出的子串就是len-i-height[i]。
可以通过预处理出前缀和,前缀和记录的是前几个后缀贡献的总子串数目;对于各个询问,在前缀和上二分查找,就能得到所的要子串了。
不过,题目还要求输出子串在原串所表示的区间,并且多个方案的情况下输出字典序最小。
那么,可以先找到一个子串S,然后在后缀数组上面再进行两次二分查找,查找到与S的LCP大于等于S长度的后缀rank上界和下界,上界和下界之间所有的子串都是满足要求的,而为了快速找到这个区间字典序最小,用个线段树RMQ一下即可。
代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define MAXN 111111 int wa[MAXN],wb[MAXN],wv[MAXN],ws[MAXN];
int cmp(int *r,int a,int b,int l){
return r[a]==r[b] && r[a+l]==r[b+l];
}
int sa[MAXN],rnk[MAXN],height[MAXN];
void SA(int *r,int n,int m){
int *x=wa,*y=wb; for(int i=0; i<m; ++i) ws[i]=0;
for(int i=0; i<n; ++i) ++ws[x[i]=r[i]];
for(int i=1; i<m; ++i) ws[i]+=ws[i-1];
for(int i=n-1; i>=0; --i) sa[--ws[x[i]]]=i; int p=1;
for(int j=1; p<n; j<<=1,m=p){
p=0;
for(int i=n-j; i<n; ++i) y[p++]=i;
for(int i=0; i<n; ++i) if(sa[i]>=j) y[p++]=sa[i]-j;
for(int i=0; i<n; ++i) wv[i]=x[y[i]];
for(int i=0; i<m; ++i) ws[i]=0;
for(int i=0; i<n; ++i) ++ws[wv[i]];
for(int i=1; i<m; ++i) ws[i]+=ws[i-1];
for(int i=n-1; i>=0; --i) sa[--ws[wv[i]]]=y[i];
swap(x,y); x[sa[0]]=0; p=1;
for(int i=1; i<n; ++i) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
} for(int i=1; i<n; ++i) rnk[sa[i]]=i;
int k=0;
for(int i=0; i<n-1; height[rnk[i++]]=k){
if(k) --k;
for(int j=sa[rnk[i]-1]; r[i+k]==r[j+k]; ++k);
}
} int n,st[17][MAXN];
void ST(int *a){
for(int i=1; i<=n; ++i) st[0][i]=a[i];
for(int i=1; i<17; ++i){
for(int j=1; j<=n; ++j){
if(j+(1<<i)-1>n) continue;
st[i][j]=min(st[i-1][j],st[i-1][j+(1<<i-1)]);
}
}
}
int logs[MAXN];
int rmq(int a,int b){
int k=logs[b-a+1];
return min(st[k][a],st[k][b-(1<<k)+1]);
}
int lcp(int a,int b){
if(a==b) return n-sa[a];
return rmq(a+1,b);
} int tree[MAXN<<2],N,x,y;
void update(int i,int j,int k){
if(i==j){
tree[k]=y;
return;
}
int mid=i+j>>1;
if(x<=mid) update(i,mid,k<<1);
else update(mid+1,j,k<<1|1);
tree[k]=min(tree[k<<1],tree[k<<1|1]);
}
int query(int i,int j,int k){
if(x<=i && j<=y){
return tree[k];
}
int mid=i+j>>1,ret=MAXN;
if(x<=mid) ret=min(ret,query(i,mid,k<<1));
if(y>mid) ret=min(ret,query(mid+1,j,k<<1|1));
return ret;
} char str[MAXN];
int a[MAXN]; long long sum[MAXN];
int dis[MAXN]; int main(){
for(int i=1; i<MAXN; ++i){
logs[i]=log2(i)+1e-6;
}
while(~scanf("%s",str)){
n=strlen(str);
for(int i=0; i<n; ++i){
a[i]=str[i]-'a'+1;
}
a[n]=0;
SA(a,n+1,28);
ST(height); memset(tree,127,sizeof(tree));
for(N=1; N<n; N<<=1);
for(int i=1; i<=n; ++i){
x=i; y=sa[i];
update(1,N,1);
} for(int i=1; i<=n; ++i){
sum[i]=n-sa[i]-height[i]+sum[i-1];
dis[i]=height[i];
} int q;
long long v,ansl=0,ansr=0;
scanf("%d",&q);
while(q--){
scanf("%I64d",&v);
long long k=(ansl^ansr^v)+1; if(k>sum[n]){
ansl=0; ansr=0;
puts("0 0");
continue;
} int tmp=lower_bound(sum+1,sum+1+n,k)-sum;
int len=dis[tmp]+k-sum[tmp-1]; int l=1,r=tmp;
while(l<r){
int mid=l+r>>1;
if(lcp(mid,tmp)>=len) r=mid;
else l=mid+1;
}
x=l; l=tmp; r=n;
while(l<r){
int mid=l+r+1>>1;
if(lcp(tmp,mid)>=len) l=mid;
else r=mid-1;
}
y=l; ansl=query(1,N,1)+1; ansr=ansl+len-1;
printf("%I64d %I64d\n",ansl,ansr);
}
}
return 0;
}
HDU5008 Boring String Problem(后缀数组 + 二分 + 线段树)的更多相关文章
- HDU5008 Boring String Problem(后缀数组)
练习一下字符串,做一下这道题. 首先是关于一个字符串有多少不同子串的问题,串由小到大排起序来应该是按照sa[i]的顺序排出来的产生的. 好像abbacd,排序出来的后缀是这样的 1---abbacd ...
- HDU 5008 Boring String Problem(后缀数组+二分)
题目链接 思路 想到了,但是木写对啊....代码 各种bug,写的乱死了.... 输出最靠前的,比较折腾... #include <cstdio> #include <cstring ...
- K-th occurrence HDU - 6704 (后缀数组+二分线段树+主席树)
大意: 给定串s, q个询问(l,r,k), 求子串s[l,r]的第kk次出现位置. 这是一篇很好的题解: https://blog.csdn.net/sdauguanweihong/article/ ...
- [CSP-S模拟测试]:platform(后缀数组+二分+线段树)
题目传送门 题目描述 走过奈何桥有一个名叫望乡台的土台,望乡台有个名曰孟婆的老妇人在卖孟婆汤.一生爱恨情仇,一世浮沉得失,都可以随这碗孟婆汤遗忘得干干净净.现在有$n$碗孟婆汤摆成一排,汤的品种不超过 ...
- HDU - 5008 Boring String Problem (后缀数组+二分法+RMQ)
Problem Description In this problem, you are given a string s and q queries. For each query, you sho ...
- 【BZOJ-4556】字符串 后缀数组+二分+主席树 / 后缀自动机+线段树合并+二分
4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 657 Solved: 274[Su ...
- CF1063F. String Journey(后缀数组+线段树)
题目链接 https://codeforces.com/contest/1063/problem/F 题解 虽然本题有时间复杂度较高但非常好写的做法...... 首先,若答案为 \(k\),则一定存在 ...
- [HEOI2016/TJOI2016]字符串(后缀数组+二分+主席树/后缀自动机+倍增+线段树合并)
后缀数组解法: 先二分最长前缀长度 \(len\),然后从 \(rnk[c]\) 向左右二分 \(l\) 和 \(r\) 使 \([l,r]\) 的 \(height\geq len\),然后在主席树 ...
- 【BZOJ4556】[Tjoi2016&Heoi2016]字符串 后缀数组+二分+主席树+RMQ
[BZOJ4556][Tjoi2016&Heoi2016]字符串 Description 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了一 ...
随机推荐
- WaxPatch中demo注意问题
问题一 https://github.com/mmin18/WaxPatch网址中提供的demo是可以运行,但是存在一个问题,如果把patch.zip换成自己的并且上传到自己的服务器(github), ...
- QRTZ_表注释
QRTZ_CALENDARS 存储Quartz的Calendar信息QRTZ_CRON_TRIGGERS 存储CronTrigger,包括Cron表达式和时区信息QRTZ_FIRED_TRIGGERS ...
- Android 添加cookie
/** * 添加cookie * * @param url */ private void setCookie(String url) { // 获取uid String uid = UserData ...
- Swift - as、as!、as?三种类型转换操作使用一览
as.as!.as? 这三种类型转换操作符的异同,以及各自的使用场景. 1,as使用场合 (1)从派生类转换为基类,向上转型(upcasts) 1 2 3 4 class Animal {} cl ...
- Enter password for default keyring to unlock
file /home/ok/.gnome2/keyrings/login.keyring /home/ok/.gnome2/keyrings/login.keyring: GNOME keyring, ...
- Spring学习笔记—装配Bean
在Spring中,对象无需自己负责查找或创建与其关联的其他对象.相反,容器负责把需要相互协作的对象引用赋予各个对象.创建应用对象之间协作关系的行为通常称为装配(wiring),这也是依赖注入的本质. ...
- EF – 6.一对一关联
5.6.6 <一对一关联概述> 5.6.7 <一对一关联CRUD演示> 在两讲视频中,首先介绍了数据库中一对一关联表的设计规范,接着通过实例介绍了如何合适Entity Fr ...
- execl一个工作薄中有几个个工作表,将这几个个工作表分别保存到不同execl文件中
用宏运行: Sub QEJebel() Dim sh As Worksheet Dim Pa As String Pa = ThisWorkbook.Path For Each ...
- APP消息推送:通知和透传
目前市场上的消息推送方式有两种:通知和透传.什么是透传?透传即是透明传送,即传送网络无论传输业务如何,只负责将需要传送的业务传送到目的节点,同时保证传输的质量即可,而不对传输的业务进行处理.透传消息, ...
- Gmail 账号找回办法
前段时间一直在用GFW代理,结果发现GOOGLE账户的保护机制起用了,要给以前的手机号发消息,结果哪个号现在不用了,所以就登陆不进去了,非常扯淡,索性谷歌了下,得出如下的解决方案,完美解决,下次直接在 ...