题意:

给一个串\(S\),\(length\leq 1e5\),\(Q\leq1e5\)个询问,每次询问输出和\(S_lS_{l+1}\dots S_r\)长得一模一样的第\(k\)个子串的开头位置。

思路:

用后缀数组处理一下,那么所有相同子串最后的\(sa\)都会靠在一起,所以找到对应的\(height\)位置,然后向左向右延伸直到\(LCP\)长度不足\(r-l+1\)(\(RMQ\)然后二分左右第一个长度比\(r-l+1\)小的位置),那么就找到了所有和\(S_lS_{l+1}\dots S_r\)长得一模一样的开始位置,然后在主席树上找第\(k\)大。

代码:

#include<map>
#include<set>
#include<queue>
#include<stack>
#include<ctime>
#include<cmath>
#include<cstdio>
#include<string>
#include<vector>
#include<cstring>
#include<sstream>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1e5 + 5;
const int INF = 0x3f3f3f3f;
const ull seed = 131;
const ll MOD = 1e9;
using namespace std;
struct node{
int lson, rson;
int num;
}T[maxn * 40];
int root[maxn], tot;
void init(){
tot = 0;
memset(T, 0, sizeof(T));
}
void update(int l, int r, int &now, int pre, int pos, int v){
T[++tot] = T[pre], now = tot;
T[now].num += v;
if(l == r) return;
int m = (l + r) >> 1;
if(pos <= m)
update(l, m, T[now].lson, T[pre].lson, pos, v);
else
update(m + 1, r, T[now].rson, T[pre].rson, pos, v);
}
int queryK(int l, int r, int now, int pre, int k){
if(l == r) return l;
int m = (l + r) >> 1, Lsum;
Lsum = T[T[now].lson].num - T[T[pre].lson].num;
if(Lsum >= k)
return queryK(l, m, T[now].lson, T[pre].lson, k);
else
return queryK(m + 1, r, T[now].rson, T[pre].rson, k - Lsum);
} int str[maxn]; //str[n]赋值一个最小值0,其他大于0
int t1[maxn], t2[maxn], c[maxn];
int sa[maxn]; //排名为i的后缀下标
int rk[maxn]; //后缀下标为i的排名
int height[maxn]; //sa[i]与sa[i - 1]的LCP
bool cmp(int *r, int a, int b, int l){
return r[a] == r[b] && r[a + l] == r[b + l];
}
void da(int *str, int n, int m){
n++;
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] = str[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]] = cmp(y, sa[i - 1], sa[i], j)? p - 1 : p++;
if(p >= n) break;
m = p;
}
int k = 0;
n--;
for(i = 0; i <= n; i++) rk[sa[i]] = i;
for(i = 0; i < n; i++){
if(k) k--;
j = sa[rk[i] - 1];
while(str[i + k] == str[j + k]) k++;
height[rk[i]] = k;
}
}
int n;
int dp[maxn][20], mm[maxn];
void initRMQ(){
mm[0] = -1;
for(int i = 1; i <= n; i++){
mm[i] = ((i & (i - 1)) == 0)? mm[i - 1] + 1 : mm[i - 1];
dp[i][0] = height[i];
}
for(int j = 1; j <= mm[n]; j++){
for(int i = 1; i + (1 << j) - 1 <= n; i++){
dp[i][j] = min(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
}
}
}
int rmq(int st, int en){
int k = mm[en - st + 1];
return min(dp[st][k], dp[en - (1 << k) + 1][k]);
}
int getL(int pos, int len){
int l = 2, r = pos;
while(l <= r){
if(l == r){
if(height[l] >= len) return l - 1;
return l;
}
int m = (l + r) >> 1;
if(rmq(m + 1, r) < len){
l = m + 1;
}
else{
r = m;
}
}
}
int getR(int pos, int len){
int l = pos, r = n;
while(l <= r){
if(l == r){
if(height[l] >= len) return l;
return l - 1;
}
int m = (l + r) >> 1;
if(rmq(l, m) < len){
r = m;
}
else{
l = m + 1;
}
}
}
int m;
char s[maxn];
int main(){
int T;
scanf("%d", &T);
while(T--){
scanf("%d%d", &n, &m);
scanf("%s", s);
int LEN = 0;
for(int i = 0; i < n; i++){
str[LEN++] = s[i] - 'a' + 1;
}
str[LEN] = 0;
da(str, LEN, 30); // for(int i = 1; i <= n; i++){
// printf("%2d * ", i);
// for(int j = sa[i]; j < LEN; j++){
// printf("%c", str[j] - 1 + 'a');
// }
// puts("");
// } init();
initRMQ();
for(int i = 1; i <= n; i++){
update(1, n, root[i], root[i - 1], sa[i] + 1, 1);
}
while(m--){
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
l--, r--;
if(n == 1){
if(k == 1) printf("1\n");
else printf("-1\n");
continue;
} int len = r - l + 1;
if(rk[l] == 1){
if(height[2] < len && k == 1) printf("%d\n", l + 1);
else if(height[2] < len) printf("-1\n");
else{
int pos = 2;
int pl = getL(pos, len), pr = getR(pos, len); if(pr - pl + 1 < k) printf("-1\n");
else printf("%d\n", queryK(1, n, root[pr], root[pl - 1], k));
}
}
else if(rk[l] == n){
if(height[n] < len && k == 1) printf("%d\n", l + 1);
else if(height[n] < len) printf("-1\n");
else{
int pos = n;
int pl = getL(pos, len), pr = getR(pos, len); if(pr - pl + 1 < k) printf("-1\n");
else printf("%d\n", queryK(1, n, root[pr], root[pl - 1], k)); }
}
else{
if(max(height[rk[l]], height[rk[l] + 1]) < len && k == 1) printf("%d\n", l + 1);
else if(max(height[rk[l]], height[rk[l] + 1]) < len) printf("-1\n");
else{
int pos = height[rk[l]] > height[rk[l] + 1]? rk[l] : rk[l] + 1;
int pl = getL(pos, len), pr = getR(pos, len); if(pr - pl + 1 < k) printf("-1\n");
else printf("%d\n", queryK(1, n, root[pr], root[pl - 1], k)); }
} }
}
return 0;
}

HDU 6704 K-th occurrence(主席树 + RMQ + 后缀数组)题解的更多相关文章

  1. BZOJ 3230: 相似子串( RMQ + 后缀数组 + 二分 )

    二分查找求出k大串, 然后正反做后缀数组, RMQ求LCP, 时间复杂度O(NlogN+logN) -------------------------------------------------- ...

  2. HDU 4417 Super Mario(主席树 区间不超过k的个数)题解

    题意:问区间内不超过k的个数 思路:显然主席树,把所有的值离散化一下,然后主席树求一下小于等于k有几个就行.注意,他给你的k不一定包含在数组里,所以问题中的询问一起离散化. 代码: #include& ...

  3. HDU 2665 Kth number(主席树静态区间第K大)题解

    题意:问你区间第k大是谁 思路:主席树就是可持久化线段树,他是由多个历史版本的权值线段树(不是普通线段树)组成的. 具体可以看q学姐的B站视频 代码: #include<cmath> #i ...

  4. HDU 4417 Super Mario(主席树求区间内的区间查询+离散化)

    Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Tota ...

  5. POJ 2104&HDU 2665 Kth number(主席树入门+离散化)

    K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 50247   Accepted: 17101 Ca ...

  6. hdu 4417 Super Mario (主席树)

    链接:http://acm.hdu.edu.cn/showproblem.php?pid=4417 题意: 给你段长为n的序列,有q个询问,每次询问区间[l.r]内有多少个数小于等于k 思路: 之前用 ...

  7. hdu 4348 To the moon (主席树)

    版权声明:本文为博主原创文章,未经博主允许不得转载. hdu 4348 题意: 一个长度为n的数组,4种操作 : (1)C l r d:区间[l,r]中的数都加1,同时当前的时间戳加1 . (2)Q ...

  8. HDU 1754 I Hate It 线段树RMQ

    I Hate It Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=175 ...

  9. HDU 2665 && POJ 2104(主席树)

    http://poj.org/problem?id=2104 对权值进行建树(这个时候树的叶子是数组b的有序数列),然后二分查找原数列中每个数在有序数列中的位置(即第几小),对每一个前缀[1,i]建一 ...

随机推荐

  1. new String("ab")到底创建了几个对象说明

    new String("ab")到底创建了几个对象? 之前一直被这个问题困扰,网上一些描述的都不是很清楚,自己看了一些资料可算搞清楚了,那就在博客上记录一下吧! String st ...

  2. 面试官:你说说ReentrantLock和Synchronized区别

    大家好!又和大家见面了.为了避免面试尴尬,今天同比较通俗语言和大家聊下ReentrantLock和Synchronized区别! 使用方式 Synchronized可以修饰实例方法,静态方法,代码块. ...

  3. (转载)微软数据挖掘算法:Microsoft 时序算法(5)

    前言 本篇文章同样是继续微软系列挖掘算法总结,前几篇主要是基于状态离散值或连续值进行推测和预测,所用的算法主要是三种:Microsoft决策树分析算法.Microsoft聚类分析算法.Microsof ...

  4. XCTF-基础Android

    前期工作 查壳,无.打开看是普通的输入注册码然后验证. 逆向分析 这题太简单了,就不贴图了.看Manifest得知MainActivity是启动类,也就是点进去输入注册码那个类.密码不重要,验证成功后 ...

  5. JavaWeb——Servlet开发

    什么是Servlet? Servlet运行的过程 Servlet的生命周期 生命周期的各个阶段 Servlet的配置 使用Web.xml配置 使用注解配置 Servlet相关接口 ServletCon ...

  6. Java中把对象、对象bean、list集合、对象数组、Map和Set以及字符串转换成Json

    对象转换为Json 对象bean转换为Json List集合转换为Json 对象数组转换为Json Map集合转换为Json Set集合转为Json 字符串转换为Json 把Java对常用的一些数据转 ...

  7. Golang之垃圾回收

    本篇主要是参考了: http://legendtkl.com/2017/04/28/golang-gc/ 说是参考,但其实基本上是原封不动. GC算法简介: 1. 引用计数 引用计数的思想非常简单:每 ...

  8. Django(多表查询操作)

    首先了解一下 mysql中的表之间的关系,一对一,一对多,多对一,多对多. 一对多关系.多对一关系.一对一关系 至少都有一侧是单个实体,所以记录之间的联系通过外键实现,让外键指向这个实体. 实现这种关 ...

  9. mapreduce编程练习(二)倒排索引 Combiner的使用以及练习

    问题一:请使用利用Combiner的方式:根据图示内容编写maprdeuce程序 示例程序 package com.greate.learn; import java.io.IOException; ...

  10. EIGRP和OSPF__邻居发现

    散知识点 1.当配置通配符时,它们的取值总是块尺寸减去1:/28的块尺寸为16,因此当我们添加网络声明时,使用了此子网号和一个在需配置的八位位组中添加值为15的通配符. 邻居发现 1.在EIGRP路由 ...