HDU - 5008:Boring String Problem (後綴數組之求不重複子串的第k大)

 

題目大意:

給你一個S串,多次詢問第k大的不重複子串。

 

解題思路:

因爲之前做過一個重複子串的第k大,所以這個題做的比較輕鬆。畢竟我感覺那個題目比這個難得多。

首先用一個數組直接儲存1 到 i這個位置所有的不重複子串和。

查詢第k大的時候直接先二分找到我們所要找的子串的起始下標。知道起始下標之後相當於我們就知道了這個串。

這個時候因爲題目有要求串相同的時候保證l r儘可能的小,這裏我認爲應該需要一些數據結構,畢竟暴力掃的話感覺很耗時誒= = 

我用的暴力寫的, 即從pos這個位置向前遍歷和向後遍歷。height始終大於當前查找串的話就不斷更新 l 的最小值,最後輸出答案即可。

剛開始long long 寫爲了int 結果T了,還以爲不能暴力,結果換了long long之後過了。。。

額,反正先寫暴力寫法吧= =

 

Ac代碼:

#include<bits/stdc++.h>
#define rank ra
using namespace std;
const int maxn=3e5+10;
const int INF=1e9+7;
typedef long long ll;
char s[maxn];
int n,m,sa[maxn],rank[maxn],height[maxn],pos[maxn];
int t1[maxn],t2[maxn],r[maxn],c[maxn];
ll sum[maxn];   //儲存不重複子串前綴和
bool cmp(int *r,int a,int b,int l)
{
    return r[a]==r[b] && r[a+l]==r[b+l];
}
void da(int str[],int sa[],int rank[],int height[],int n,int m)
{
    n++;
    int i,j,p,*x=t1,*y=t2;
    for(int i=0;i<m;i++) c[i]=0;
    for(int i=0;i<n;i++) c[x[i]=str[i]]++;
    for(int i=1;i<m;i++) c[i]+=c[i-1];
    for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
    for(int j=1;j<=n;j<<=1)
    {
        p=0;
        for(int i=n-j;i<n;i++) y[p++]=i;
        for(int i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
        for(int i=0;i<m;i++) c[i]=0;
        for(int i=0;i<n;i++) c[x[y[i]]]++;
        for(int i=1;i<m;i++) c[i]+=c[i-1];
        for(int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1,x[sa[0]]=0;
        for(int i=1;i<n;i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        if(p>=n) break;
        m=p;
    }
    int k=0;
    n--;
    for(int i=0;i<=n;i++) rank[sa[i]]=i;
    for(int i=0;i<n;i++)
    {
        if(k) k--;
        j=sa[rank[i]-1];
        while(str[i+k]==str[j+k]) k++;
        height[rank[i]]=k;
    }
}
int main()
{
    while(scanf(" %s",s)!=EOF)
    {
        int ls=strlen(s),len=0;
        for(int i=0;i<ls;i++) r[len++]=s[i];
        r[len]=0;n=len;
        da(r,sa,rank,height,n,128);
        for(int i=1;i<=n;i++)   //計算前綴和
            sum[i]=n-sa[i]-height[i]+sum[i-1];
        scanf("%d",&m);
        int l=0,r=0;
        ll q=0;
        while(m--)
        {
            scanf("%lld",&q);
            q=(l^r^q)+1;
            int pos=lower_bound(sum+1,sum+1+n,q)-sum;   //二分找到位置
            if(sum[n]<q)
            {
                l=0,r=0;
                printf("0 0\n");
                continue;
            }
            l=sa[pos];  //處理出l和長度
            q-=sum[pos-1];
            int gk=height[pos]+q;
            for(int i=pos;i>=1;i--)     //向前向後找最小
            {
                if(height[i]>=gk)
                    l=min(l,sa[i]);
                else break;
            }
            for(int i=pos;i<n;i++)
            {
                if(height[i+1]>=gk)
                    l=min(l,sa[i+1]);
                else break;
            }
            r=l+gk-1;
            l++,r++;    //輸出答案
            printf("%d %d\n",l,r);
        }
    }
    return 0;
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章