2019CCPC 네트워크 레이스 C - K 번째 발생 HDU - 6704 (접미사 배열 ST + + + 표 이진 트리 회장)

문제의 의미

간격 (L)을 추구하는 본래 문자열 k 번째 발생 서브 캐릭터 위치 r에.

링크 : https://vjudge.net/contest/322094#problem/C

생각

접미사 자동 장치와 게임을 썼을 때, TLE 게임을 종료합니다.

접미사 배열을 배우고 발견 후 접미사 배열이 문제는 쓸 간단합니다.

우리는 샘플 aaabaabaaaab 접미사 정렬을 나열 :

예를 들어, 우리가 제 2 및도 3은 문자열 AA로 표현 찾을 L은 R은, k는 2,3,2 인이고, 가로 배열 접미사 배열은 접미어의 긴 공통 프리픽스의 두 행 (정렬) 인접를 나타낸다 길이 방향이 생각 [L은 R] 문자열은 접미사 접두사이어야한다. 우리는 제 6 용이 전에 공통 프리픽스 후 발견 될 수 RK [2] 6 인 RK [L] (L 접미사 개시)를 찾아 공통 프리픽스> = (R-L + 1)이 있다면 접두사는 우리가 찾고있는 문자열을 충족해야한다. 그리고 당신은 공통 접두어 길이 접미사 정렬 찾을 수는 단조, 더 가까이 여기에 더 이상 일반적인 접두사 (6), 멀리보다 일반적인 접두사 6 짧은 반 우리는 상부 및 하부 경계를 찾을 수 있습니다, 당신은 테이블 ST를 조회 할 수 있습니다 높이 [RK [RK [Y] X + 1] ~] 최소 긴 공통 프리픽스 길이 접미사 X 및 Y 접미어를 찾는.

다음 후 찾아,이 접미사는 우리가 모든 사 [I] (원래 문자열의 i 번째 접미사 분류 위치)에 삽입 회장 나무를 진행하고 찾을 조건을 충족 할 수있는 그 작은 K-경계에서 할 수 있습니다.

코드

#include<bits/stdc++.h>
const int N = 2e5 + 10;
using namespace std;
char s[N];
int len, M, rk[N], sa[N], tax[N], tp[N];
/*
sa[i]:排名为i的后缀的位置
rk[i]:从第i个位置开始的后缀的排名,把从第i个位置开始的后缀简称为后缀i
tp[i]:基数排序的第二关键字,意义与sa一样,即第二关键字排名为i的后缀的位置
tax[i]:i号元素出现了多少次。辅助基数排序
s:字符串,s[i]表示字符串中第i个字符串
*/
void jsort() //基数排序
{
    for (int i = 0; i <= M; i++) tax[i] = 0;
    for (int i = 1; i <= len; i++) tax[rk[i]]++;
    for (int i = 1; i <= M; i++) tax[i] += tax[i - 1];
    for (int i = len; i >= 1; i--) sa[ tax[rk[tp[i]]]-- ] = tp[i];
}
void suffixSort() //后缀排序
{
    M = 75; //字符集的大小
    for (int i = 1; i <= len; i++) rk[i] = s[i] - 'a' + 1, tp[i] = i;
    jsort();
    // Debug();
    for (int w = 1, p = 0; p < len; M = p, w <<= 1)
    {
        //w:当前倍增的长度,w = x表示已经求出了长度为x的后缀的排名,现在要更新长度为2x的后缀的排名
        //p表示不同的后缀的个数,很显然原字符串的后缀都是不同的,因此p = N时可以退出循环
        p = 0;//这里的p仅仅是一个计数器000
        for (int i = 1; i <= w; i++) tp[++p] = len - w + i;
        for (int i = 1; i <= len; i++) if (sa[i] > w) tp[++p] = sa[i] - w; //这两句是后缀数组的核心部分,我已经画图说明
        jsort();//此时我们已经更新出了第二关键字,利用上一轮的rak更新本轮的sa
        swap(tp, rk);//这里原本tp已经没有用了
        rk[sa[1]] = p = 1;
        for (int i = 2; i <= len; i++)
            rk[sa[i]] = (tp[sa[i - 1]] == tp[sa[i]] && tp[sa[i - 1] + w] == tp[sa[i] + w]) ? p : ++p;
        //这里当两个后缀上一轮排名相同时本轮也相同
        //Debug();
    }
    //  for (int i = 1; i <= len; i++)
    //      printf("%d ", sa[i]);
}
//i号后缀:从i开始的后缀
//lcp(x,y):字符串x与字符串y的最长公共前缀,在这里指x号后缀与与y号后缀的最长公共前缀
int height[N];//lcp(sa[i],sa[i-1]),即排名为i的后缀与排名为i-1的后缀的最长公共前缀
int h[N];//height[rak[i]],即i号后缀与它前一名的后缀的最长公共前缀
//性质:H[i]>=H[i-1]-1
void getHeight()
{
    int j, k = 0;
    for(int i = 1; i <= len; i++)
    {
        if(k) k--;
        int j = sa[rk[i] - 1];
        while(s[i + k] == s[j + k]) k++;
        h[i]=height[rk[i]] = k;
        //printf("%d\n", k);
    }
}
/*
两个后缀的最大公共前缀lcp(x,y)=min(height[rank[x+1]~rank[y]]), 用rmq维护,O(1)查询
可重叠最长重复子串:height数组里的最大值
本质不同的子串的数量:枚举每一个后缀,第i个后缀对答案的贡献为len-sa[i]+1-height[i]
*/
/**********************主席树*********************/
int n, q, sz, num = 0;
int T[N];
int sum[N<<5], L[N<<5], R[N<<5];
#define mid (l+r)/2
inline int build(int l, int r)
{
    int rt = ++ num;
    sum[rt] = 0;
    if (l < r)
    {
        L[rt] = build(l, mid);
        R[rt] = build(mid+1, r);
    }
    return rt;
}

inline int update(int pre, int l, int r, int x)
{
    int rt = ++ num;
    L[rt] = L[pre];
    R[rt] = R[pre];
    sum[rt] = sum[pre]+1;
    if (l < r)
    {
        if (x <= mid) L[rt] = update(L[pre], l, mid, x);
        else R[rt] = update(R[pre], mid+1, r, x);
    }
    return rt;
}

inline int query(int u, int v, int l, int r, int k)
{
    if (l >= r) return l;
    int x = sum[L[v]] - sum[L[u]];
    if (x >= k) return query(L[u], L[v], l, mid, k);
    else return query(R[u], R[v], mid+1, r, k-x);
}
/*********************ST表**************/
int mm[N],dpMin[N][20];
//初始化Rmq,b数组下标从1开始,从0开始简单修改
void initRmq(int n,int b[])  //O(nlogn)预处理
{
    mm[0]=-1;
    for(int i=1;i<=n;i++)
    {
        mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
        dpMin[i][0]=b[i];
    }
    for(int j=1;j<=mm[n];j++)
    {
        for(int i=1;i+(1<<j)-1<=n;i++)
        {
            dpMin[i][j]=min(dpMin[i][j-1],dpMin[i+(1<<(j-1))][j-1]);
        }
    }
}
int rmqMin(int x,int y)
{
    int k=mm[y-x+1];
    return min(dpMin[x][k],dpMin[y-(1<<k)+1][k]);
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(sum,0,sizeof(sum));
        memset(height,0,sizeof(height));
        memset(dpMin,0,sizeof(dpMin));
        memset(mm,0,sizeof(mm));
        num=0;
        int n,m;
        scanf("%d%d",&n,&m);
        scanf("%s",s+1);
        len = strlen(s + 1);
        suffixSort();
        T[0]=build(1,n);
        for(int i=1;i<=len;i++)
        {
            T[i]=update(T[i-1],1,n,sa[i]);
        }
        getHeight();
        initRmq(n,height);
        while(m--)
        {
            int x,y,k;
            scanf("%d%d%d",&x,&y,&k);
            int L=rk[x],R=rk[x];
            int l=1,r=rk[x],g=y-x+1;
            while(l<=r)
            {
                int h=(l+r)>>1;
                if(h+1<=rk[x]&&rmqMin(h+1,rk[x])>=g)
                {
                    r=h-1;
                    L=h;
           //         cout<<L<<" GG"<<endl;
                }
                else
                    l=h+1;
            }
      //      cout<<L<<endl;
            l=rk[x]+1,r=n;
            while(l<=r)
            {
                int h=(l+r)>>1;
                if(h>=rk[x]+1&&rmqMin(rk[x]+1,h)>=g)
                {
                    l=h+1;
                    R=h;
                }
                else
                    r=h-1;
            }
     //       printf("%d %d %d\n",rk[x],L,R);
            if(R-L+1<k)
            {
                printf("-1\n");
            }
            else
            {
                printf("%d\n",query(T[L-1],T[R],1,n,k));
            }
        }
    }

    return 0;
}
/*
10
14 5
abcabcdabcddee
1 2 3
2 2 4
1 3 1
7 7 2
1 6 2
13 10
aabccaadeaaaa
*/

추천

출처www.cnblogs.com/mcq1999/p/11499025.html