2019 ccpc网络赛 hdu6704 K-th occurrence

题意:给你一个字符串,再给你q个询问,每一个询问有l,r,k, 求字符串中下标l到下标r这个子串第k次出现在字符串中的位置,不存在则输出-1.

后缀数组:将所有后缀排序,height数组是比较第i个后缀和第i-1个后缀的最长公共前缀的长度

rmq:求区间的最小/最大数

主席树:求区间第k大的数

首先求出后缀数组(包括height函数),对于每一个询问,我们知道它的开始下标是i,通过后缀数组中的rk[i](表示下标为i的后缀排完序后对应的位置)得到在排好序的所有后缀的位置,现在通过h函数找到公共前缀长度>=r - l + 1的上限和下限,但是如果直接找最坏情况会变成O(n),优化方法是因为h函数的特殊性,懂后缀数组的都知道h[i]>=h[i-1]-1,那么我们先把所有区间的h的最小值用ST表统计出来,然后二分2~rk[l] 和 rk[l + 1] + 1 ~ n找上下限(二分条件是h[mid]>=r - l + 1),找到上下限后,我们现在要求第k个的位置,用主席树预处理下就行了,然后按照主席树的方法查询答案即可。

注意点:上下限的边界情况,如果rk[2]满足,那么下限是1,如果rk[l + 1]不满足,那么上限是rk[l]

 

/**/
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cctype>
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <string>
#include <stack>
#include <queue>

typedef long long LL;
using namespace std;

int n, t, q, m, cnt;
char s[100005];
int rt[100005], sa[100005], x[100005], y[100005], c[100005], h[100005], rk[100005];
int dp[25][100005], bin[25], Log[100005];

struct node
{
    int l, r;
    int w;
}tr[100005 * 20];

void read(int &x){
	char ch = getchar();x = 0;
	for (; ch < '0' || ch > '9'; ch = getchar());
	for (; ch >='0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
}

void cpy(int &now, int old){
    now = ++cnt;
    tr[now] = tr[old];
}

void up(int &now){
    tr[now].w = tr[tr[now].l].w + tr[tr[now].r].w;
}

void build(int &now, int l, int r){
    now = ++cnt;
    tr[now].w = 0;
    if(l == r) return ;
    int mid = (l + r) >> 1;
    build(tr[now].l, l, mid);
    build(tr[now].r, mid + 1, r);
}

void update(int &now, int old, int l, int r, int x){
    cpy(now, old);
    if(l == r && x == l){
        tr[now].w++;
        return ;
    }
    int mid = (l + r) >> 1;
    if(x <= mid) update(tr[now].l, tr[old].l, l, mid, x);
    else update(tr[now].r, tr[old].r, mid + 1, r, x);
    up(now);
}

int query(int s, int t, int l, int r, int k){
    if(l == r) return l;
    int mid = (l + r) >> 1;
    int ans = tr[tr[t].l].w - tr[tr[s].l].w;
    if(k <= ans) return query(tr[s].l, tr[t].l, l, mid, k);
    else return query(tr[s].r, tr[t].r, mid + 1, r, k - ans);
}

void get_sa(){
    for (int i = 1; i <= n; i++) ++c[x[i] = s[i] - 'a' + 1];
    for (int i = 2; i <= m; i++) c[i] += c[i - 1];
    for (int i = n; i >= 1; i--) sa[c[x[i]]--] = i;
    for (int k = 1; k <= n; k <<= 1){
    int cnt1 = 0;
    for (int i = n - k + 1; i <= n; i++) y[++cnt1] = i;
    for (int i = 1; i <= n; i++) if(sa[i] > k) y[++cnt1] = 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 >= 1; i--) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
    swap(x, y);
    x[sa[1]] = 1, cnt1 = 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] ? cnt1 : ++cnt1);
      if(cnt1 == n) break;
      m = cnt1;
    }
}

void get_h(){
    int k = 0;
    for (int i = 1; i <= n; i++) rk[sa[i]] = i;
    for (int i = 1; i <= n; i++){
      if(rk[i] == 1) continue;
      if(k) k--;
      int j = sa[rk[i] - 1];
      while(i + k <= n && j + k <= n && s[i + k] == s[j + k]) k++;
      h[rk[i]] = k;
    }
}

int rmq1(int l, int r, int len){
	int ans = r, R = r;
	while(l <= r){
		int mid = (l + r) >> 1;
		int L = mid, tp = Log[R - L + 1];
		int minn = min(dp[tp][L], dp[tp][R - bin[tp] + 1]);
		if(minn >= len) ans = mid, r = mid - 1;
		else l = mid + 1;
	}
	return ans;
}

int rmq2(int l, int r, int len){
	int ans = l, L = l;
	while(l <= r){
		int mid = (l + r) >> 1;
		int R = mid, tp = Log[R - L + 1];
		int minn = min(dp[tp][L], dp[tp][R - bin[tp] + 1]);
		if(minn >= len) ans = mid, l = mid + 1;
		else r = mid - 1;
	}
	return ans;
}

int main()
{
	//freopen("in.txt", "r", stdin);
	//freopen("out.txt", "w", stdout);

	bin[0] = 1;
	for (int i = 1; i <= 20; i++) bin[i] = bin[i - 1] << 1;
	Log[0] = -1;
	for (int i = 1; i <= 100000; i++) Log[i] = Log[i / 2] + 1;
	scanf("%d", &t);
	while(t--){
		memset(c, 0, sizeof(c));
		// memset(h, 0, sizeof(h));
		scanf("%d %d", &n, &q);
		scanf("%s", s + 1);
		m = 26;
		get_sa();
		get_h();
		cnt = 0;
        build(rt[0], 1, n);
        for (int i = 1; i <= n; i++) update(rt[i], rt[i - 1], 1, n, sa[i]);
        for (int i = 1; i <= n; i++) dp[0][i] = h[i];
        for (int i = 1; i <= Log[n]; i++){
        	for (int j = 1; j + bin[i] - 1 <= n; j++){
        		dp[i][j] = min(dp[i - 1][j], dp[i - 1][j + bin[i - 1]]);
        	}
        }
	    for (int i = 1, l, r, k; i <= q; i++){
	    	scanf("%d %d %d", &l, &r, &k);
	    	int tl = rmq1(2, rk[l], r - l + 1), tr = rmq2(rk[l] + 1, n, r - l + 1);
	    	if(h[tl] >= r - l + 1) tl--;
	    	if(h[tr] < r - l + 1) tr--;
	    	if(tr - tl + 1 < k) printf("-1\n");
	    	else printf("%d\n", query(rt[tl - 1], rt[tr], 1, n, k));
	    }
	}

	return 0;
}
/**/

 

全部评论

相关推荐

秋招进行到现在终于能写总结了。完全没想到战线会拉这么长,过程会如此狼狈,不过更应该怪自己太菜了。好在所有的运气都用在了最后,也是有个去处。背景:双2本硕科班,无竞赛,本科一段研究所实习,硕士一段大厂暑期实习但无转正。技术栈是C++&nbsp;&amp;&nbsp;Golang,实习是客户端音视频(而且是鸿蒙端开发),简历两个C++项目一个Golang项目。主要投递岗位:后端,cpp软开,游戏服务端,测开,以及一些不拘泥于Java的岗位。从8月起总共投递123家公司,笔试数不清了,约面大约30家。offer/oc/意向:友塔游戏(第一个offer,面试体验很好,就是给钱好少南瑞继保(计算机科班点击就送(限男生),不...
乡土丁真真:佬很厉害,羡慕~虽然我还没有到校招的时候,也想讲一下自己的看法:我觉得不是CPP的问题,佬的背书双2,技术栈加了GO,有两段实习。投了123,面了30.拿到11个offer。这个数据已经很耀眼了。这不也是CPP带来的吗?当然也不止是CPP。至少来说在这个方向努力过的也会有好的结果和选择。同等学历和项目选java就会有更好的吗?我个人持疑问态度。当然CPP在方向选择上确实让人头大,但是我觉得能上岸,至于最后做什么方向,在我看来并不重要。至于CPP特殊,有岗位方向的随机性,java不是不挑方向,只是没得选而已。也希望自己以后校招的时候能offer满满
点赞 评论 收藏
分享
10-25 12:05
已编辑
湖南科技大学 Java
若梦难了:我有你这简历,已经大厂乱杀了
点赞 评论 收藏
分享
冲芭芭拉鸭:你这图还挺新,偷了。
投递美团等公司10个岗位
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务