hdu5008(后缀数组求第k小的字串)

题目:求串的第k小的子串。

思路分析:串长为100000,时间复杂度应该为O(nlogn),用后缀数组解。这是我的第二道后缀数组题,看了人家代码才写出来的,height数组用的很灵活,每个后缀的所有前缀就是全部的子串,但是有一些是相同的,去重就用到了height数组,对于每一个后缀有n-sa[i]个子串,然后二分定位到串i,第k小的串在后缀i中,然后求出串长,在利用height求最小的l。

代码如下:


#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<math.h>
#include<cstring>
#include<string>
#define LL __int64
#define N 100005
#define inf 0x3f3f3f3f
#define pi acos(-1.0)
#define eps 10e-6
using namespace std;
char s[N];
int sa[N],t[N],t2[N],c[N],r[N];
void build_sa(int n,int m)
{
    int i,k,*x = t,*y = t2;
    for(i = 0; i < m; i++)  c[i] = 0;
    for(i = 0; i < n; i++)  c[ x[i] = r[i] ]++;
    for(i = 1; i < m; i++)  c[i] += c[i-1];
    for(i = n-1; i >= 0; i--)   sa[ --c[ x[i] ] ] = i;

    for(k = 1; k <= n; k<<=1)
    {
        int p = 0;
        for(i = n-k; i < n; i++)    y[p++] = i;
        for(i = 0; i < n; i++)  if(sa[i] >= k)  y[p++] = sa[i] - k;

        for(i = 0; i < m; i++)  c[i] = 0;
        for(i = 0; i < n; i++)  c[ x[ y[i] ] ]++;
        for(i = 1; i < m; i++)  c[i] += c[i-1];
        for(i = n-1; i >= 0; i--)   sa[ --c[ x[ y[i] ] ] ] = y[i];

        swap(x,y);
        p = 1; x[ sa[0] ] = 0;
        for(i = 1; i < n; i++)
            x[ sa[i] ] = y[ sa[i-1] ] == y[ sa[i] ] && y[ sa[i-1]+k ] == y[ sa[i]+k ] ? p-1 : p++;
        if(p >= n) break;
        m = p;
    }
}
int rank[N],height[N];
void getheight(int n)
{
    int i,j,k = 0;
    for(i = 0; i <= n; i++)  rank[ sa[i] ] = i;
    for(i = 0; i < n; i++)  {
        if(k) k--;
        j = sa[ rank[i]-1 ];
        while(s[i+k] == s[j+k]) k++;
        height[ rank[i] ] = k;
    }
}
LL cnt[N];
int main()
{
    while(scanf("%s",s) != EOF)
    {
        int n = strlen(s);
        int i;
        for(i = 0; i < n; i++)
            r[i] = s[i] - 'a' + 1;
        r[n] = 0;
        build_sa(n+1,28);
        getheight(n);
        cnt[0] = -1;
        for(i = 1; i <= n; i++)     cnt[i] = n - sa[i] - height[i];
        for(i = 2; i <= n; i++)     cnt[i] += cnt[i-1];
        int l = 0,r = 0;
        LL k;
        int cas;
        scanf("%d",&cas);
        while(cas--)
        {
            scanf("%I64d",&k);
            k = (k^l^r)+1;
            if(k > cnt[n]){
                l = r = 0;
                printf("%d %d\n",l,r);
                continue;
            }
            int cur = lower_bound(cnt+1,cnt+n+1,k) - cnt;
         //   cout<<cur<<" "<<sa[cur]<<endl;
            if(cur > n) {
                l = r = 0;
            }
            else {
                LL ll = sa[cur];
                LL rr = n - (cnt[cur] - k)-1;
                int tot = rr-ll+1;
                l = ll;
                for(i = cur+1; i <= n; i++)
                {
                    if(height[i] < tot)  break;
                    if(sa[i] < l)
                        l = sa[i];
                }
                r = l+tot-1;
                r++;l++;
            }
            printf("%d %d\n",l,r);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值