2018暑期集训 子串查询(主席树+后缀数组)

 

题面:

单点时限: 2.0 sec

内存限制: 512 MB

现在Jack有一个只由只由小写字母组成的字符串S,现在他想进行下面的查询:
查询S中[l,r]子串第2次出现时第一个字母的位置。

输入格式

T组输入
对于每组输入,第一行输入一个n,m代表S的长度和查询的次数。
接下来m行,每行输入一组l,r,代表查询的子串。
(1≤n,m≤100000,1≤l≤r≤n)

输出格式

对于每组查询,输出子串l,r第2次出现的位置,如果没有出现,输出−1。

样例

input

1
5 5
aaabcc
1 2
1 3
4 4
5 5
1 5

output

2
-1
-1
6
-1


暴力hash居然可以水过去,不过还有更优的解法,其实可以吧这个题变得更难一些,即查询第$k$次出现的位置。利用ST表维护下后缀排序后的公共长度的最小值,然后二分找出两端合法的位置,主席树维护下排序后的序列,这样我们就把问题转化为经点的主席树第K大,而本题的K就是2。

AC代码

#include 
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pairpii;

const int N = (int) 1e5 * 2 + 11;
const int M = (int) 1e6 + 11;
const int MOD = (int) 1e9 + 7;
const int INF = (int) 0x3f3f3f3f;
const ll INFF = (ll) 0x3f3f3f3f3f3f3f3f;
/*-----------------------------------------------------------*/

int n, k;
int Rank[N + 1], sa[N + 1], height[N + 1], tmp[N + 1];
int MIN[N + 1][40];

bool cmp_sa(int i, int j){
    if(Rank[i] != Rank[j]) return Rank[i] < Rank[j];
    else {
        int ri = i + 2 <= n ? Rank[i + 2] : -1;
        int rj = j + 2 <= n ? Rank[j + 2] : -1;
        return ri < rj;
    }
}

void SA(string s){ 
    n = s.size();
    for(int i = 0; i <= n; i++){
        sa[i] = i; Rank[i] = (i < n) ? s[i] : -1;
    }
    for(k = 1; k <= n; k <<= 1){
        sort(sa, sa + n + 1, cmp_sa);
        tmp[sa[0]] = 0;
        for(int i = 1; i <= n; i++){
            tmp[sa[i]] = tmp[sa[i - 1]] + (cmp_sa(sa[i - 1], sa[i]) ? 1 : 0);
        }
        for(int i = 0; i <= n; i++){
            Rank[i] = tmp[i];
        }
    }
    for(int i = 0; i <= n; i++) Rank[sa[i]] = i;
    int h = 0;
    height[0] = 0;
    for(int i = 0; i < n; i++){
        int j = sa[Rank[i] - 1];
        if(h > 0) h--;
        for(;j + h < n && i + h < n; h++){
            if(s[j + h] != s[i + h]) break;
        }
        height[Rank[i] - 1] = h;
    } 
    for(int i = 1; i <= n; i++) MIN[i][0] = height[i];
    for(int j = 1; (1 << j) <= n; j++){
        for(int i = 1; i <= n; i++){
            MIN[i][j] = min(MIN[i][j - 1], MIN[i + (1 << (j - 1))][j - 1]);
        }
    }
}

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

char SS[N + 10];
const int MAXN = 2e5 + 11; 
int root[MAXN<<5],lson[MAXN<<5],rson[MAXN<<5],sum[MAXN<<5],sz;
void Build(int& rt,int le,int ri){
    rt=++sz;   sum[rt]=0;
    if(le==ri) return ;
    int mid=(le+ri)>>1;
    Build(lson[rt],le,mid);
    Build(rson[rt],mid+1,ri);
}
void Update(int pre,int &rt,int le,int ri,int val){
    rt=++sz; 
    lson[rt]=lson[pre],rson[rt]=rson[pre],sum[rt]=sum[pre]+1; 
    if(le==ri) return ;
    int mid=(le+ri)>>1;
    if(val<=mid) Update(lson[pre],lson[rt],le,mid,val);
    else Update(rson[pre],rson[rt],mid+1,ri,val);
}
int Query(int st,int ed,int le,int ri,int k){
    if(le==ri) return le;
    int mid=(le+ri)>>1;
    int t=sum[lson[ed]]-sum[lson[st]] ;
    if(k<=t) Query(lson[st],lson[ed],le,mid,k);
    else Query(rson[st],rson[ed],mid+1,ri,k-t);
}

int main(){
    
    int T; scanf("%d", &T);
    while(T--){
        int  q; scanf("%d%d", &n, &q);
        scanf("%s", SS);
        SA((string) SS);
        
        sz = 0;
        Build(root[0], 1, n);
         
        for(int i = 1; i <= n; i++){
            Update(root[i - 1], root[i], 1, n, sa[i] + 1);
        }  
        
        while(q--){
            int l, r; 
            scanf("%d%d", &l, &r);        
            int x;
            if(l > r) swap(l, r );
            int P = Rank[l - 1];
            
            int L = 2, R = P, L1 = P + 1, R1 = P;
            while (L <= R)
            {
                int M = L + R >> 1;
                if (LCP(M, P) < r - l + 1)
                    L = M + 1;
                else
                    R = M - 1, L1 = M;
            }
            L = P + 1, R = n;
            while (L <= R)
            {
                int M = L + R >> 1;
                if (LCP(P + 1, M) < r - l + 1)
                    R = M - 1;
                else
                    L = M + 1, R1 = M;
            }
            --L1;     
            if(R1 - L1  + 1 < 2) puts("-1");
            else 
                printf("%d\n", Query(root[L1 - 1], root[R1], 1, n, 2));
        } 
    }    
    return 0;
}

 

你可能感兴趣的:(动态规划--LIS)