poj 2758 后缀数组

题意:
给一个字符串,提供两种操作,操作I:在 当前 第i个字符之前插入字符c;操作Q:求 原序列 后缀i,与后缀j的LCP,计算LCP时应把插入字符一起算上

思路:
确实没看出来这玩意能用后缀数组搞……
啊本题有好几个坑需要额外注意:
1. 插入时候的位置指的上当前序列;
2. 询问的位置指的是原序列,然后插入字符应计入比较;
3. 题目仅仅是说 Engligh Letter 而非 lower case letter,wa了半天才找到 - -|||;
4. I操作的步数非常少,最多200次
先抛开插入操作考虑询问操作,求一个序列任意两个后缀的LCP,显然后缀数组加RMQ,实现方式为对height数组做预处理,查询RMQ(rank[i],rank[j]);然后考虑插入,因为插入的操作数非常少,对于询问,可以先找到i和j的原序列LCP,再找到最近的一个插入在i,j后的字符距离i,j的位置k(这里找到最近的插入字符位置我是用set自带的平衡二叉树来加速查询),分情况讨论。
1. 如果LCP < k-1,显然插入字符对查询值无影响;
2. 如果LCP >= k-1,则从k位置开始,用t1,t2逐个指向当前序列中的字符,直至t1,,t2指向的均为原序列中的字符为止,然后递归查询t1,t2
本题在思路上不是特别麻烦,难点在看清题目的表述,以及实现能力上

#include <iostream>  
#include <algorithm>  
#include <stdio.h>  
#include <string.h>  
#include <vector>  
#include <set>  
using namespace std;  

const int MAXINT  = 0x7fffffff;  
const int MAXSIZE = 5 * 1e4 + 100;  

vector<char> ins[MAXSIZE];  
set<int> inset;  

#define rep(i,n) for(int i = 0; i < n; i++)  

int rk[MAXSIZE], sa[MAXSIZE], height[MAXSIZE], wa[MAXSIZE], res[MAXSIZE];  
char w[MAXSIZE];  
int len;  

void getSa(int up) {  
    int *k = rk, *id = height, *r = res, *cnt = wa;  
    rep(i, up) cnt[i] = 0;  
    rep(i, len) cnt[k[i] = w[i]]++;  
    rep(i, up) cnt[i + 1] += cnt[i];  
    for (int i = len - 1; i >= 0; i--) {  
        sa[--cnt[k[i]]] = i;  
    }  
    int d = 1, p = 0;  
    while (p < len){  
        for (int i = len - d; i < len; i++) id[p++] = i;  
        rep(i, len)  if (sa[i] >= d) id[p++] = sa[i] - d;  
        rep(i, len) r[i] = k[id[i]];  
        rep(i, up) cnt[i] = 0;  
        rep(i, len) cnt[r[i]]++;  
        rep(i, up) cnt[i + 1] += cnt[i];  
        for (int i = len - 1; i >= 0; i--) {  
            sa[--cnt[r[i]]] = id[i];  
        }  
        swap(k, r);  
        p = 0;  
        k[sa[0]] = p++;  
        rep(i, len - 1) {  
            if (sa[i] + d < len && sa[i + 1] + d < len && r[sa[i]] == r[sa[i + 1]] && r[sa[i] + d] == r[sa[i + 1] + d])  
                k[sa[i + 1]] = p - 1;  
            else k[sa[i + 1]] = p++;  
        }  
        if (p >= len) return;  
        d <<= 1, up = p, p = 0;  
    }  
}  

void getHeight() {  
    int i, k, h = 0;  
    rep(i, len) rk[sa[i]] = i;  
    rep(i, len) {  
        if (rk[i] == 0)  
            h = 0;  
        else {  
            k = sa[rk[i] - 1];  
            if (h) h--;  
            while (w[i + h] == w[k + h]) h++;  
        }  
        height[rk[i]] = h;  
    }  
}  

void getSuffix() {  
    len = strlen(w);  
    int up = 0;  
    rep(i, len) {  
        w[i] = w[i] - 'A' + 1;  
        up = up > w[i] ? up : w[i];  
    }  
    w[len] = 0;  
    getSa(up + 1);  
    getHeight();  
}  

int A[MAXSIZE];  
int dmin[MAXSIZE][32];  

void RMQ_init(int A[], int len){  
    //len 数组长度  
    for (int i = 0; i<len; ++i){  
        dmin[i][0] = A[i];  
    }  
    for (int j = 1; (1 << j) <= len; ++j)  
    for (int i = 0; i + (1 << j) - 1<len; ++i){  
        dmin[i][j] = min(dmin[i][j - 1], dmin[i + (1 << (j - 1))][j - 1]);  
    }  
    return;  
}  

int RMQ_min(int L, int R){  
    int k = 0;  
    while (1 << (k + 1) <= R - L + 1) k++;  
    return min(dmin[L][k], dmin[R - (1 << k) + 1][k]);  
}  

void sinsert(char c, int p){  
    bool flag = false;  
    int cnt = 0;  
    int k = -1;  
    for (int i = 0;i<len;++i){  
        if (cnt + ins[i].size() < p - 1) cnt += ins[i].size();  
        else{  
            k = p - cnt - 2;  
            if (ins[i].size() != 0){  
                ins[i].push_back(ins[i][ins[i].size()-1]);  
                for (int j = ins[i].size() - 1; j > k + 1; --j)  
                    ins[i][j] = ins[i][j-1];  
                ins[i][k+1] = c;  
            }  
            else  
                ins[i].push_back(c);  

            inset.insert(i);  
            //cout<<"Insert: "<<i<<endl;  
            flag = true;  
            break;  
        }  
        cnt++;  
    }  
    if (!flag){  
        if (cnt + ins[len].size() < p) ins[len].push_back(c);  
        else{  
            k = p - cnt - 2;  
            if (ins[len].size() != 0)  
                for (int j = ins[len].size() - 1; j > k; --j)  
                    ins[len][j+1] = ins[len][j];  
            ins[len][k+1] = c;  
        }  
        inset.insert(len);  
        //cout<<"Insert: "<<len<<endl;  
    }  
}  

int query(int s1,int s2){  
    //cout<<"query: "<<s1<<" "<<s2<<endl;  
    int ans = 0;  
    if (s1 == s2){  
        for (int i = s1 + 1;i<len;++i){  
            ans+=ins[i].size();  
        }  
        ans += len-s1;  
        ans += ins[len].size();  
        return ans;  
    }  
    if (s1>s2) swap(s1,s2);  
    int lcp;  
    if (rk[s1] < rk[s2]) lcp = RMQ_min(rk[s1] + 1,rk[s2]);  
        else lcp = RMQ_min(rk[s2] + 1,rk[s1]);  
    ans = lcp;  

    //cout<<"RMQ_lcp: "<<ans<<endl;  

    int t = MAXINT;  
    set<int>::iterator t1 = inset.upper_bound(s1);  
    set<int>::iterator t2 = inset.upper_bound(s2);  
    if (t1!=inset.end())  
        t = t < *t1-s1 ? t : *t1-s1;  
    if (t2!=inset.end())  
        t = t < *t2-s2 ? t : *t2-s2;  

    //cout<<"t: "<<t<<endl;  

    if (lcp > t-1){  
        int i = s1 + t;  
        int j = s2 + t;  
        int i1 = 0, j1 = 0;  
        ans = t;  
        bool flag = true;  
        while (flag && (ins[j].size() != 0 || ins[i].size() != 0)){  
            char ic, jc;  
            if (i1 < ins[i].size()){  
                ic = ins[i][i1];  
                i1++;  
            }  
            else{  
                if (i == len) {flag = false;break;}  
                ic = w[i];  
                i++;  
                i1 = 0;  
            }  
            //cout<<"ic: "<<ic<<endl;  
            if (j1 < ins[j].size()){  
                jc = ins[j][j1];  
                j1++;  
            }  
            else{  
                if (j == len) {flag = false;break;}  
                jc = w[j];  
                j++;  
                j1 = 0;  
            }  
            //cout<<"jc: "<<jc<<endl;  

            if (ic == jc) ans++;  
            else flag = false;  
            //if (i == len || j == len) flag = false;  
        }  
        if (!flag) return ans;  
        else {  
            return ans+query(i,j);  
        }  
    }  
    else return ans;  
}  

int main(){  
    int a,b;  
    char temp[5];  
    scanf("%s",w);  
    getSuffix();  
    RMQ_init(height,len);  
    int n;  
    scanf("%d",&n);  
    for (int i=0;i<n;++i){  
        char c;  
        scanf("%s",temp);  
        if (temp[0] == 'Q'){  
            scanf("%d %d",&a,&b);  
            int ans = query(a-1,b-1);  
            printf("%d\n", ans);  
        }  
        else{  
            c = getchar();  
            c = getchar();  
            scanf("%d",&a);  
            sinsert(c - 'A' + 1,a);  
        }  
    }  
    return 0;  
}  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
POJ 2182是一道使用树状数组解决的题目,题目要求对给定的n个数进行排序,并且输出每个数在排序后的相对位置。树状数组是一种用来高效处理前缀和问题的数据结构。 根据引用中的描述,我们可以通过遍历数组a,对于每个元素a[i],可以使用二分查找找到a到a[i-1]中小于a[i]的数的个数。这个个数就是它在排序后的相对位置。 代码中的query函数用来求前缀和,add函数用来更新树状数组。在主函数中,我们从后往前遍历数组a,通过二分查找找到每个元素在排序后的相对位置,并将结果存入ans数组中。 最后,我们按顺序输出ans数组的元素即可得到排序后的相对位置。 参考代码如下: ```C++ #include <iostream> #include <cstdio> using namespace std; int n, a += y; } } int main() { scanf("%d", &n); f = 1; for (int i = 2; i <= n; i++) { scanf("%d", &a[i]); f[i = i & -i; } for (int i = n; i >= 1; i--) { int l = 1, r = n; while (l <= r) { int mid = (l + r) / 2; int k = query(mid - 1); if (a[i > k) { l = mid + 1; } else if (a[i < k) { r = mid - 1; } else { while (b[mid]) { mid++; } ans[i = mid; b[mid = true; add(mid, -1); break; } } } for (int i = 1; i <= n; i++) { printf("%d\n", ans[i]); } return 0; } ``` 这段代码使用了树状数组来完成题目要求的排序功能,其中query函数用来求前缀和,add函数用来更新树状数组。在主函数中,我们从后往前遍历数组a,通过二分查找找到每个元素在排序后的相对位置,并将结果存入ans数组中。最后,我们按顺序输出ans数组的元素即可得到排序后的相对位置。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [poj2182Lost Cows——树状数组快速查找](https://blog.csdn.net/aodan5477/article/details/102045839)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [poj_2182 线段树/树状数组](https://blog.csdn.net/weixin_34138139/article/details/86389799)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值