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;
}
/**/