2019ccpc网络赛 HDU 6704 K-th occurrence(后缀数组+RMQ+主席树)

本文介绍了一种使用后缀数组解决字符串子串查询问题的方法。通过排序所有子串并利用二分查找和RMQ(Range Minimum Query)来确定目标子串在字符串中的位置。算法涉及后缀数组的构造、汉明距离计算以及权值主席树的数据结构,对于字符串处理和算法设计有较高的参考价值。
摘要由CSDN通过智能技术生成

题意:给一个长度为n的字符串s,然后有q个询问,每次询问求出l到r之间的字串在s数组第k次出现的位置;

思路:用后缀数组对所有子串进行排序,用二分和RMQ找到包含该子串的排名区间(含公共前缀的字串一定连续,二分左边界和右边界),用权值主席树查找第k次出现的子串的位置即可;

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<string>
#include<cstring>
#include<bitset> 
#include<vector>
#include<queue>
#include<stack>
#include<set>
#include<map>
#include<iomanip>
#include<algorithm>
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define endl "\n"
#define PI  acos(-1)
//CLOCKS_PER_SEC clock()函数每秒执行次数
using namespace std;

const int INF = 0x3f3f3f3f;
const int N = 1e5+5;
int mod = 1e9 +7;
int n,m,k;
int a,b,e;
char s[N];
int str[N];
int sa[N],rk[N],h[N],c[N],x[N],y[N];
void get_sa(){
	for(int i = 1 ; i <= m ; ++i ) c[i] = 0;
	for(int i = 1 ; i <= n ; ++i ) c[x[i] = str[i]]++;
	for(int i = 2 ; i <= m ; ++i ) c[i] += c[i - 1];
	for(int i = n ; i ;  i--) sa[c[x[i]]--] = i;
	for(int k = 1 ; k <= n ; k <<= 1){
		int num = 0;
		for(int i = n - k + 1 ; i <= n ; ++i ) y[++num] = i;
		for(int i = 1 ; i <= n ; ++i ){
			if(sa[i] > k){
				y[++num] = sa[i] - k;
			}
		}
		for(int i = 1 ; i <= m ; ++i ) c[i] = 0;
		for(int i = 1 ; i <= n ; ++i ) c[x[i]]++;
		for(int i = 2 ; i <= m ; ++i ) c[i] += c[i - 1];
		for(int i = n ; i ; i--) sa[c[x[y[i]]]--] = y[i],y[i] = 0;
		swap(x,y);
		x[sa[1]] = 1,num = 1;
		for(int i = 2 ; i <= n ; ++i ){
			x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? num : ++num;
		}
		if(num == n) break;
		m = num;
	}
}
void get_h(){
	for(int i = 1 ; i <= n ; ++i ) rk[sa[i]] = i;
	for(int i = 1,k = 0; i <= n; ++i ){
		if(rk[i] == 1) continue;
		if(k) k--;
		int j = sa[rk[i] - 1];
		while(j + k <= n && i + k <= n && str[i + k] == str[j + k]) k++;
		h[rk[i]] = k;
	}
}
int root[N];
struct node{
	int l,r,cnt;
}tr[N * 22];
int f[N][22];
void init(){
	for(int i = 1 ; i <= n ; ++i ) f[i][0] = h[i];
	for(int i = 1 ; i < 22 ; ++i ){
		for(int j = 1 ; j + (1 << i) - 1 <= n ; ++j ){
			f[j][i] = min(f[j][i - 1],f[j + (1 << i - 1)][i - 1]); 
		}
	}
}
int get(int l,int r){
	int t = log2(r - l + 1.0);
    return min(f[l][t], f[r - (1 << t) + 1][t]);

}
bool check1(int mid,int d){
	return get(mid + 1,a) >= d;
}
bool check2(int mid,int d){
	return get(a + 1,mid) >= d;
}
int idx;
int build(int l,int r){
	int u = ++idx;
	tr[u].cnt = 0;
	if(l == r) return u;
	int mid = l + r >> 1;
	tr[u].l = build(l,mid);
	tr[u].r = build(mid + 1,r);
	return u;
}
void pushup(int u){
	tr[u].cnt = tr[tr[u].l].cnt + tr[tr[u].r].cnt;
}
int insert(int p,int l,int r,int k){
	int u = ++idx;
	tr[u] = tr[p];
	if(l == r){
		tr[u].cnt++;
		return u;
	}
	int mid = l + r >> 1;
	if(k <= mid) tr[u].l = insert(tr[p].l,l,mid,k);
	else tr[u].r = insert(tr[p].r,mid + 1,r,k);
	pushup(u);
	return u;
}
int query(int p,int q,int l,int r,int k){
	if(!q) return -1;
	if(l == r) return l;
	int mid = l + r >> 1;
	int t = tr[tr[q].l].cnt - tr[tr[p].l].cnt;
	if(t >= k) return query(tr[p].l,tr[q].l,l,mid,k);
	return query(tr[p].r,tr[q].r,mid + 1,r,k - t);
}
void solve(){
	idx = 0;
	scanf("%d%d%s",&n,&k,s + 1);
	m = 30;
	for(int i = 1 ; i <= n ; ++i ) str[i] = s[i] - 'a' + 1;
	get_sa();
	get_h();
	init();
	root[0] = build(1,n);
	for(int i = 1 ; i <= n ; ++i ){
		root[i] = insert(root[i - 1],1,n,sa[i]);
	}
	while(k--){
		scanf("%d%d%d",&a,&b,&e);
		int d = b - a + 1;
		a = rk[a];
		int l = 1,r = a;
		while(l < r){
			int mid = l + r >> 1;
			if(check1(mid,d)) r = mid;
			else l = mid + 1;
		}
		int nl = l;
		if(get(a,a) < d) nl = a;
		l = a + 1,r = n;
		while(l < r){
			int mid = l + r + 1 >> 1;
			if(check2(mid,d)) l = mid;
			else r = mid - 1;
		}
		int nr = l;
		if(get(a + 1,a + 1) < d) nr = a;
		if(nr - nl + 1 < e) puts("-1");
		else printf("%d\n",query(root[nl - 1],root[nr],1,n,e));
	}
	for(int i = 1 ; i <= idx ; ++i ) tr[i].cnt = tr[i].l = tr[i].r = 0;
}

signed main(){
	int tt;
	scanf("%d",&tt);
	while(tt--)
	solve();
	return 0;
}
/*
*
*  ┏┓   ┏┓+ +
* ┏┛┻━━━┛┻┓ + +
* ┃       ┃
* ┃   ━   ┃ ++ + + +
*  ████━████+
*  ◥██◤ ◥██◤ +
* ┃   ┻   ┃
* ┃       ┃ + +
* ┗━┓   ┏━┛
*   ┃   ┃ + + + +Code is far away from  
*   ┃   ┃ + bug with the animal protecting
*   ┃    ┗━━━┓ 神兽保佑,代码无bug 
*   ┃        ┣┓
*    ┃        ┏┛
*     ┗┓┓┏━┳┓┏┛ + + + +
*    ┃┫┫ ┃┫┫
*    ┗┻┛ ┗┻┛+ + + +
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值