Problem Description

You are given a string S consisting of only lowercase english letters and some queries.

For each query (l,r,k), please output the starting position of the k-th occurence of the substring SlSl+1...Sr in S.

Input

The first line contains an integer T(1≤T≤20), denoting the number of test cases.

The first line of each test case contains two integer N(1≤N≤105),Q(1≤Q≤105), denoting the length of S and the number of queries.

The second line of each test case contains a string S(|S|=N) consisting of only lowercase english letters.

Then Q lines follow, each line contains three integer l,r(1≤l≤r≤N) and k(1≤k≤N), denoting a query.

There are at most 5 testcases which N is greater than 103.

Output

For each query, output the starting position of the k-th occurence of the given substring.

If such position don't exists, output −1 instead.

Sample Input

2
12 6
aaabaabaaaab
3 3 4
2 3 2
7 8 3
3 4 2
1 4 2
8 12 1
1 1
a
1 1 1

Sample Output

5
2
-1
6
9
8
1

思路:

我们要找l~r的字串的兄弟串不难想到求lcp 通过二分我们可以找到最右和最左的排名 然后我们只要用可持久线段树维护下标  然后求下标第k小的字符串即可

#include <bits/stdc++.h>
using namespace std;
const double pi = acos(-1.0);
const int N = 1e5+1000;
const int inf = 0x3f3f3f3f;
const double eps = 1e-6;
typedef long long ll;
const ll mod = 1e9+7;
int rt[N];
struct tree{
int l,r,v,ls,rs;
}t[N<<5];
int nico;
void build(int &p,int l,int r){
p=++nico;
t[p].l=l; t[p].r=r;
if(l==r){
return ;
}
int mid=(l+r)>>1;
build(t[p].ls,l,mid);
build(t[p].rs,mid+1,r);
}
void update(int &p,int last,int x){
p=++nico;
t[p]=t[last];
t[p].v++;
if(t[p].l==t[p].r&&t[p].l==x){
t[p].v=1;
return ;
}
int mid=(t[p].l+t[p].r)>>1;
if(x<=mid) update(t[p].ls,t[last].ls,x);
else update(t[p].rs,t[last].rs,x);
}
int query(int p,int last,int k){
if(t[p].l==t[p].r){
return t[p].l;
}
int tmp=t[t[p].ls].v-t[t[last].ls].v;
int mid=(t[p].l+t[p].r)>>1;
if(tmp>=k) return query(t[p].ls,t[last].ls,k);
else return query(t[p].rs,t[last].rs,k-tmp);
}
struct S_array{
int s[N],sa[N],t[N],t2[N],c[N],n;
int f[N][20];
void build_sa(int m){
int i,*x=t,*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(int k=1;k<=n;k<<=1){
int p=0;
for(i=n-k;i<n;i++)y[p++]=i;
for(i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k;
for(i=0;i<m;i++)c[i]=0;
for(i=0;i<n;i++)c[x[y[i]]]++;
for(i=0;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]+k]==y[sa[i]+k]?p-1:p++;
if(p>=n)break;
m=p;
}
}
int rank[N],height[N];
void getHeight(){
int i,j,k=0;
for(i=0;i<n;i++)rank[sa[i]]=i;
for(i=0;i<n;i++){
if(k)k--;
int j=sa[rank[i]-1];
while(s[i+k]==s[j+k])k++;
height[rank[i]]=k;
}
}
void rmq(){
for(int i=1;i<n;i++) f[i][0]=height[i];
for(int j=1;j<20;j++)
for(int i=1;i+(1<<j)-1<n;i++)
f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
int lcp(int l,int r){
int k=log2(r-l+1);
return min(f[l][k],f[r+1-(1<<k)][k]);
}
void bu(){
for(int i=1;i<n;i++)
update(rt[i],rt[i-1],sa[i]);
}
int work(int l,int r,int k){
int po=rank[l-1];
//cout<<po<<endl;
int L,R,ans=-1; int mxl,mxr;
L=1; R=po; mxl=mxr=po;
while(L<=R){
int mid=(L+R)>>1;
// cout<<L<<" "<<R<<" "<<mid<<" "<<lcp(mid,po)<<endl;
if(lcp(mid,po)>=r-l+1){
R=mid-1;
ans=mid;
}else{
L=mid+1;
}
}
if(ans!=-1)
mxl=ans;
ans=-1;
if(po<n-1){
L=po+1; R=n-1;
while(L<=R){
// cout<<L<<" "<<R<<endl;
int mid=(L+R)>>1;
if(lcp(po+1,mid)>=r-l+1){
L=mid+1;
ans=mid;
}else{
R=mid-1;
}
}
if(ans!=-1)
mxr=ans;
}
// cout<<n<<endl;
// cout<<mxl<<" "<<mxr<<endl;
if(lcp(mxl,mxr)>=r-l+1){
mxl=mxl-1;
}
if(mxr-mxl+1<k) return -1;
return query(rt[mxr],rt[mxl-1],k)+1;
}
}sa;
char s[N];
int main(){
// ios::sync_with_stdio(false);
// cin.tie(0); cout.tie(0);
int t; scanf("%d",&t);
while(t--){
int n,q; scanf("%d%d",&n,&q);
scanf("%s",s);
nico=0;
for(int i=0;i<n;i++)
sa.s[i]=s[i]-'a'+1;
sa.s[n]=0; sa.n=n+1;
sa.build_sa(27); sa.getHeight();
build(rt[0],0,N);
sa.rmq(); sa.bu();
for(int i=1;i<=q;i++){
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",sa.work(l,r,k));
}
}
}

hdu 6704 K-th occurrence(后缀数组+可持久化线段树)的更多相关文章

  1. luogu P3919 [模板]可持久化数组(可持久化线段树/平衡树)(主席树)

    luogu P3919 [模板]可持久化数组(可持久化线段树/平衡树) 题目 #include<iostream> #include<cstdlib> #include< ...

  2. HDU 5558 Alice's Classified Message(后缀数组+二分+rmq(+线段树?))

    题意 大概就是给你一个串,对于每个\(i\),在\([1,i-1]\)中找到一个\(j\),使得\(lcp(i,j)\)最长,若有多个最大\(j\)选最小,求\(j\)和这个\(lcp\)长度 思路 ...

  3. Luogu3732 [HAOI2017] 供给侧改革 【后缀数组】【线段树】【乱搞】

    题目分析: 这道题我是乱搞的,因为他说$01$串是随机的. 那么我们可以猜测能够让LCP变大的地方很少.求出后缀数组之后可能让LCP变大的地方就等价于从大到小往height里动态加点同时维护这个点左右 ...

  4. Luogu5289 十二省联考2019字符串问题(后缀数组+拓扑排序+线段树/主席树/KDTree)

    先考虑80分做法,即满足A串长度均不小于B串,容易发现每个B串对应的所有A串在后缀数组上都是一段连续区间,线段树优化连边然后判环求最长链即可.场上就写了这个. 100分也没有什么本质区别,没有A串长度 ...

  5. 【后缀数组】【线段树】poj3974 Palindrome

    考虑奇数长度的回文,对于字符串上的每个位置i,如果知道从i开始的后缀和到i为止的前缀反转后的字符串的lcp长度的话,也就知道了以第i个字符为对称中心的最长回文的长度了.因此,我们用在S中不会出现的字符 ...

  6. Luogu P3919 【模板】可持久化数组 可持久化线段树

    其实就是可持久化线段树的模板题线段树不会看这里 #include<bits/stdc++.h> ; using namespace std; ]; ],rc[N*],val[N*],cnt ...

  7. HDU - 6704 K-th occurrence (后缀数组+主席树/后缀自动机+线段树合并+倍增)

    题意:给你一个长度为n的字符串和m组询问,每组询问给出l,r,k,求s[l,r]的第k次出现的左端点. 解法一: 求出后缀数组,按照排名建主席树,对于每组询问二分或倍增找出主席树上所对应的的左右端点, ...

  8. hdu 3553 Just a String (后缀数组)

    hdu 3553 Just a String (后缀数组) 题意:很简单,问一个字符串的第k大的子串是谁. 解题思路:后缀数组.先预处理一遍,把能算的都算出来.将后缀按sa排序,假如我们知道答案在那个 ...

  9. 【BZOJ3110】【整体二分+树状数组区间修改/线段树】K大数查询

    Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c 如果是2 a b c形式,表示询问从第a个位置到第b个位 ...

随机推荐

  1. python的默认参数的一个坑

    前言 pass 正文 在 https://docs.python.org/3/tutorial/controlflow.html#default-argument-values 中,有这样一段话 Im ...

  2. Both Dolby Atmos driver and API need to be installed问题的一个解决方法

    问题的原因在于缺少以下两个部分: Dolby Atmos driver:指你的声卡驱动中自带的杜比文件 如果驱动里没有,说明你的硬件可能不支持杜比,或者驱动太老没有包含杜比. Dolby Atmos ...

  3. 编译安装 nginx -1.14.2

    编译安装 nginx -1.14.2 1 ) 下载nginx-1.14.2 源码包: wget http://nginx.org/download/nginx-1.14.2.tar.gz 2 ) 编译 ...

  4. 18.java设计模式之中介者模式

    基本需求 智能家庭包括各种设备,闹钟.咖啡机.电视机.窗帘等 要看电视时,各个设备可以协同工作,自动完成看电视的准备工作,比如流程为:闹铃响起->咖啡机开始做咖啡->窗帘自动落下-> ...

  5. python--or 和 and 表达式

    or表达式: 两边为一真一假,返回真: 两边都为假,返回右边: 两边都为真,返回左边: and表达式: 两边为一真一假,返回假: 两边都为假,返回左边: 两边都为真,返回右边:

  6. 记一次 RocketMQ broker 因内存不足导致的启动失败

    原创:西狩 编写日期 / 修订日期:2020-01-12 / 2020-01-12 版权声明:本文为博主原创文章,遵循 CC BY-SA-4.0 版权协议,转载请附上原文出处链接和本声明. 背景 该小 ...

  7. SW3516中文资料书

    SW3516 是一款高集成度的快充车充芯片, 支持 A+C 口任意口快充输出, 支持双口独立限流.其集成了 5A 高效率同步降压变换器, 支持 PPS/PD/QC/AFC/FCP/SCP/PE/SFC ...

  8. Python入门之修改jupyter启动目录

    [导读]在给大家分享知识的过程中,我们也会分享一些小技巧,能够帮助大家在学习过程中有更好的体验.之前我们给大家分享了anaconda安装教程以及jupyter notebook使用方法,今天我们为大家 ...

  9. 生僻标签 fieldset 与 legend 的妙用

    谈到 <fieldset> 与 <legend>,大部分人肯定会比较陌生,在 HTML 标签中,属于比较少用的那一批. 我最早知道这两个标签,是在早年学习 reset.css ...

  10. SharePoint Online 站点模板中权限的设置

    SharePoint Online可以使用PnP引擎部署站点模板.这个模板的功能非常强大,除了可以定制主题,外观以外,还可以生成list并导入数据,生成文档库,导入文档等等. 今天重点说一下其中的站点 ...