PowerOJ 2904: kmlver的子串plus

8 篇文章 0 订阅
6 篇文章 0 订阅
题意:

kmlver有一个字符串S, 该字符串仅由小写字母组成,你需要分别回答Q次询问,每次询问由4个整数组成: l 1 , r 1 , l 2 , r 2 ( 1 ≤ l 1 ≤ r 1 ≤ ∣ S ∣ , 1 ≤ l 2 ≤ r 2 ≤ ∣ S ∣ ) l1, r1, l2, r2 (1 ≤ l1 ≤ r1 ≤ |S|, 1 ≤ l2 ≤ r2 ≤ |S|) l1,r1,l2,r2(1l1r1S1l2r2S),你需要统计子串 S l 1 S_{l_1} Sl1 S l 1 + 1 S_{l_1+1} Sl1+1 S l 1 + 2 S_{l_1+2} Sl1+2 S r 1 S_{r1} Sr1在子串 S l 2 S_{l_2} Sl2 S l 2 + 1 S_{l_2+1} Sl2+1 S l 2 + 2 S_{l_2+2} Sl2+2 S r 2 S_{r2} Sr2的出现次数。

题解:

我们可以先用后缀数组处理S串,则子串A在S串中的出现次数,即为S中所有满足与A串的LCP ≥ len(A)的后缀的个数。

因为两个后缀的LCP是它们在后缀数组中height[l + 1, r]的最小值(区间最值问题直接用RMQ即可), 那么我们先找到A串在后缀数组中的rank,然后二分找到区间,使排名在这个区间中每个后缀都满足与A串的LCP ≥ len(A),接下来就简单了,我们只需要知道排名在这个区间中的后缀有多少个满足它的SA是在 [ l 2 , r 2 − ( r 1 − l 1 + 1 ) + 1 ] [l2, r2 - (r1 - l1 + 1) + 1] [l2,r2(r1l1+1)+1]中,这个直接用主席树查找即可。

代码:
/*
 * @Author : Nightmare
 */
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define ls 2 * rt
#define rs 2 * rt + 1
#define gcd(a,b) __gcd(a,b)
#define eps 1e-6
#define lowbit(x) (x&(-x))
#define N 100005
#define M 1000005
#define mod 10007
#define inf 0x3f3f3f3f
char str[N]; int root[N], tot;
struct SA{
    static const int maxn = 1e5 + 5;
    int h[maxn], rk[maxn], sa[maxn], y[maxn], c[maxn];
    void build(int n, int m) {
        for (int i = 0; i <= m; i ++) c[i] = 0;
        for (int i = 1; i <= n; i ++) c[rk[i] = (str[i] - 'a' + 1)] ++;
        for (int i = 1; i <= m; i ++) c[i] += c[i - 1];
        for (int i = n; i >= 1; i --) sa[c[rk[i]]--] = i;
        for (int k = 1; k <= n; k <<= 1) {
            int p = 0;
            for (int i = n - k + 1; i <= n; i ++) y[++p] = i;
            for (int i = 1; i <= n; i ++) if (sa[i] > k) y[++p] = sa[i] - k;
            for (int i = 0; i <= m; i ++) c[i] = 0;
            for (int i = 1; i <= n; i ++) c[rk[i]]++;
            for (int i = 1; i <= m; i ++) c[i] += c[i - 1];
            for (int i = n; i >= 1; i --) sa[c[rk[y[i]]]--] = y[i];
            swap(rk, y); rk[sa[1]] = p = 1;
            for (int i = 2; i <= n; i ++)
                rk[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k] ? p : ++p);
            if (p >= n) break;
            m = p;
        }
        for (int i = 1, k = 0; i <= n; i ++) {
            if (k) -- k; int j = sa[rk[i] - 1];
            while (str[i + k] == str[j + k]) k ++;
            h[rk[i]] = k;
        }
    }
}sa;
struct RMQ{
    static const int maxn = 1e5 + 5;
    static const int maxm = 20;
    int f[maxn][maxm], lg[maxn];
    inline void init(int n){
        for(int i = 1 ; i <= n ; i ++) f[i][0] = sa.h[i];
        for(int i = 2 ; i <= n ; i ++) lg[i] = lg[i >> 1] + 1;
        for(int j = 1 ; (1 << j) <= n ; 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]);
    }
    inline int query(int l, int r){
        if(l > r) swap(l, r); l ++;
        int k = lg[r - l + 1];
        return min(f[l][k], f[r - (1 << k) + 1][k]);
    }
}rmq;
struct Segment_Tree{
    static const int maxn = 1e5 + 5;
    struct node{ int l, r, sum; }t[maxn * 32];
    void update(int l, int r, int &x, int y, int k){
        t[++tot] = t[y]; t[tot].sum ++; x = tot;
        if(l == r) return ;
        int mid = (l + r) >> 1;
        if(k <= mid) update(l, mid, t[x].l, t[y].l, k);
        else update(mid + 1, r, t[x].r, t[y].r, k);
    }
    int query(int l, int r, int x, int y, int k){
        if(l == r) return l;
        int mid = (l + r) >> 1, sum = t[t[y].l].sum - t[t[x].l].sum;
        if(k <= sum) return query(l, mid, t[x].l, t[y].l, k);
        else return query(mid + 1, r, t[x].r, t[y].r, k - sum);
    }
    int rk(int l,int r,int x,int y,int k) {
        if (k == 0) return 0;
        if (l == r) return t[y].sum - t[x].sum;
        int mid = l + r >> 1, sum = 0;
        if (mid >= k) sum = rk(l, mid, t[x].l, t[y].l, k);
        else sum = t[t[y].l].sum - t[t[x].l].sum + rk(mid + 1, r, t[x].r, t[y].r, k);
        return sum;
    }
}seg;
inline int get_l(int x, int len, int n){
    int l = 1, r = x, ans = x;
    while(l <= r){
        int mid = (l + r) >> 1;
        if(rmq.query(mid, x) >= len) r = mid - 1, ans = mid;
        else l = mid + 1;
    }
    return ans;
}
inline int get_r(int x, int len, int n){
    int l = x, r = n, ans = x;
    while(l <= r){
        int mid = (l + r) >> 1;
        if(rmq.query(x, mid) >= len || mid == x) l = mid + 1, ans = mid;
        else r = mid - 1;
    }
    return ans;
}
inline void solve(){
    int n; scanf("%d", &n);
    scanf("%s", str + 1);
    sa.build(n, 30);
    rmq.init(n);
    for(int i = 1 ; i <= n ; i ++) seg.update(1, n, root[i], root[i - 1], sa.sa[i]);
    int Q; scanf("%d", &Q);
    for(int cas = 1 ; cas <= Q ; cas ++){
        int l1, r1, l2, r2; scanf("%d %d %d %d", &l1, &r1, &l2, &r2);
        if(r1 - l1 > r2 - l2){ puts("0"); continue; }
        int l = get_l(sa.rk[l1], r1 - l1 + 1, n), r = get_r(sa.rk[l1], r1 - l1 + 1, n);
        int ansl = seg.rk(1, n, root[l - 1], root[r], l2 - 1);
        int ansr = seg.rk(1, n, root[l - 1], root[r], r2 - (r1 - l1 + 1) + 1);
        cout << ansr - ansl << '\n';
    }
}
signed main(){
#ifndef ONLINE_JUDGE
    freopen("D:\\in.txt", "r", stdin);
#endif
    solve();
#ifndef ONLINE_JUDGE
    cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << " s.\n";
#endif
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值