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;
}

 

 

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