HDU 4691

卡了两天的题,今天终于想明白到底是哪里出了问题了。原来只是因为 j+(1<<i)-1<=n 我理解成了 j+(1<<i)<n。乍看之下还真以为这是对的呢,问了好多人都被骗了。

说说题吧。题目的描述很清晰,一道后缀数组的题,我第一次写后缀数组,也是看了好多资料,以及题解才勉强写出来,可是就是因为刚刚那个大坑,浪费了两天的时间。也不能说是浪费吧,至少每一次找错,都是在加深我对算法和代码的理解。

后缀数组最重要的是对后缀子序列的排序,求出h[],之后再写一个rmq就可以O(1)时间求出两个后缀的LCP。

下面的是代码。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 100010;
#define max(a,b) (a > b ? a : b)
#define min(a,b) (a < b ? a : b)

char c[maxn];
int h[maxn], rank[maxn], sa[maxn], t1[maxn], t2[maxn], z[maxn];
int ll[maxn], rmq[40][maxn], rmq2[maxn][40];

bool cmp(int tmp[], int a, int b, int t)
{
    return tmp[a] == tmp[b] && tmp[a+t] == tmp[b+t];
}

void suffix(int len, int sum)
{
    int *x = t1;
    int *y = t2;
    len++;
    for(int i = 0; i < sum; i++) z[i] = 0;
    for(int i = 0; i < len; i++) z[x[i] = c[i]]++;
    for(int i = 1; i < sum; i++) z[i] += z[i-1];
    for(int i = len -1; i >= 0; i--) sa[--z[x[i]]] = i;
    for(int j = 1; j <= len; j <<= 1)
    {
        int p = 0;
        for(int i = len - j; i < len; i++) y[p++] = i;
        for(int i = 0; i < len; i++) if(sa[i] >= j) y[p++] = sa[i] - j;

        for(int i = 0; i < sum; i++) z[i] = 0;
        for(int i = 0; i < len; i++) z[x[y[i]]]++;
        for(int i = 1; i < sum; i++) z[i] += z[i - 1];
        for(int i = len - 1; i >= 0; i--) sa[--z[x[y[i]]]] = y[i];

        swap(x,y);
        p = 1;
        x[sa[0]] = 0;
        for(int i = 1; i < len; i++)
            x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p-1: p++;
        if(p >= len)
            break;
        sum = p;
    }
    int k = 0;
    len--;
    for(int i = 0; i <= len; i++) rank[sa[i]] = i;
    for(int i = 0; i < len; i++)
    {
        int j = sa[rank[i] - 1];
        while(c[i + k] == c[j + k]) k++;
        h[rank[i]] = k;
        if(k) k--;
    }
}

void init()
{
     ll[0]= -1;
    for(int i = 1; i < maxn; i++)
        if((i & (i - 1)) == 0)
            ll[i] = ll[i - 1] + 1;
        else
            ll[i] = ll[i - 1];
}

/*void init_rmq(int n)
{
    for(int i=1;i<=n;i++)rmq[0][i]=h[i];
    for(int i=1;i<=ll[n];i++)
        for(int j=1;j+(1<<i)-1<=n;j++)
        {
            int a=rmq[i-1][j];
            int b=rmq[i-1][j+(1<<(i-1))];
            if(a<b)rmq[i][j]=a;
            else rmq[i][j]=b;
        }
}*/

void init_rmq(int n)
{
    for(int i = 1; i <= n; i++)
        rmq[0][i] = h[i];
        int i, j;
    for(i=1; i<=ll[n];i++)
    {
        for(j=1;(1<<i)+j-1<=n ;j++)
        {
            if(rmq[i-1][j] < rmq[i-1][j+(1<<(i-1))])
            rmq[i][j] = rmq[i-1][j] ;
            else
            rmq[i][j] = rmq[i-1][j+(1<<(i - 1))];
        }
    }
}

int qury_rmq(int ql, int qr)
{
    int t =ll[qr - ql + 1];
    qr -= (1 << t) - 1;
    return min(rmq[t][ql], rmq[t][qr]);
}

int cal(int t)
{
    if(t == 0)
    return 1;
    int ret = 0;
    while(t)
    {
        ret++;
        t /= 10;
    }
    return ret;
}

int main()
{
    freopen("in.txt","r",stdin);
    freopen("out(2).txt","w",stdout);
    init();
    while(scanf("%s", c) == 1)
    {
        int len = strlen(c);
        c[len] = 0;
        suffix(len, 128);
        init_rmq(len);
        long long ans1 = 0, ans2 = 0;
        int q;
        cin >> q;
        int prel, prer;
        cin >> prel >> prer;
        ans1 += (prer - prel + 1);
        ans2 += (prer - prel + 3);
        q--;
        int same = 0;
        while(q--)
        {
            int l, r;
            scanf("%d%d", &l, &r);
            if(l == prel)
                same = maxn * 100;
            else
                same = qury_rmq(min(rank[l], rank[prel]) + 1, max(rank[l], rank[prel]));

                //cout << same << endl;
            same = min(prer - prel, same);
            same = min(r - l, same);

            ans1 += r - l + 1;
            ans2 += r - l - same + 2 + cal(same);
            //cout << same << " " << l << " " << r << endl;
            prel = l;
            prer = r;
        }
        printf("%I64d %I64d
", ans1, ans2);
    }
}
View Code
原文地址:https://www.cnblogs.com/ye8370/p/3894614.html