hdu-4691 最长公共前缀-后缀数组

8 篇文章 0 订阅
4 篇文章 0 订阅

http://acm.hdu.edu.cn/showproblem.php?pid=4691

http://blog.csdn.net/fire_cat11211022/article/details/9908545  详细的后缀数组有关问题的解答


解析:当然用后缀数组最方便,在后缀数组中有很多重要的定义和性质,现在我们来认识一些:

定义:LCP(i,j)=suffix(SA[i])与suffix[SA[j]]的最长公共前缀长度,即排号序后的后缀中第i名和第j名的最长公共前缀长度。

然后我们再用一个重要的性质就可以求出LCP(i,j)了,性质描述:LCP(i,j)=min{LCP(k-1,k)}  i<k<=j

而对于LCP(k-1,k)我们有height数组啊,比如我们要求suffix(i)和suffix(j)的最长公共前缀,就相当于求height[rank[i]]到height[rank[j]]之间的最小值。

至于区间求最小值,线段树是个很好的选择,到此问题圆满解决。


#include <algorithm>//sort
#include <cstring>//memset
#include <cstdio>
#include <iostream>
#define MAX 2000100
using namespace std;
const int MAX_SFX = 210000;
struct Sfx
{
    int i;
    int key[2];
    bool operator < (const Sfx& s) const
    {
        return key[0] < s.key[0]
               || key[0] == s.key[0] && key[1] < s.key[1];
    }
};

typedef struct
{
    int l;
    int r;
    int minn;
} Tree;
Tree tree[MAX*4];
int build(int i,int l,int r) //建树
{
    int mid;
    tree[i].l=l;
    tree[i].r=r;
    tree[i].minn=MAX;
    if(l==r)
    {
        return 0;
    }
    mid=(l+r)/2;
    build(i*2,l,mid);
    build(i*2+1,mid+1,r);
}
int insert(int i,int k,int v) //插入
{
    tree[i].minn=v < tree[i].minn? v:tree[i].minn;
    if(tree[i].l==tree[i].r)
        return 0;
    else
    {
        int mid=(tree[i].l+tree[i].r)/2;
        if(mid<k)
            insert(i*2+1,k,v);
        else
            insert(i*2,k,v);
    }
}
int findmin(int i,int x,int y)  //找区间的最小
{
    int ans=MAX,mid;
    if((x<=tree[i].l)&&(y>=tree[i].r))
    {
        return tree[i].minn;
    }
    ans=MAX;
    mid=(tree[i].l+tree[i].r)/2;
    if (x<=mid)  ans=min(ans,findmin(i*2,x,y));
    if (y>mid)   ans=min(ans,findmin(i*2+1,x,y));
    return ans;
}
int g_buf[MAX_SFX + 1];
Sfx g_tempSfx[2][MAX_SFX], *g_sa = g_tempSfx[0];
void cSort(Sfx* in, int n, int key, Sfx* out)
{
    int* cnt = g_buf;
    memset( cnt, 0, sizeof(int) * (n + 1) );
    for (int i = 0; i < n; i++)
    {
        cnt[ in[i].key[key] ]++;
    }
    for (int i = 1; i <= n; i++)
    {
        cnt[i] += cnt[i - 1];
    }
    for (int i = n - 1; i >= 0; i--)
    {
        out[ --cnt[ in[i].key[key] ] ] = in[i];
    }
}
void buildSA(char* text, int len)
{
    Sfx *temp = g_tempSfx[1];
    int* rank = g_buf;
    for (int i = 0; i < len; i++)
    {
        g_sa[i].i = g_sa[i].key[1] = i;
        g_sa[i].key[0] = text[i];
    }
    sort(g_sa, g_sa + len);
    for (int i = 0; i < len; i++)
    {
        g_sa[i].key[1] = 0;
    }
    int wid = 1;
    while (wid < len)
    {
        rank[ g_sa[0].i ] = 1;
        for (int i = 1; i < len; i++)
        {
            rank[ g_sa[i].i ] = rank[ g_sa[i - 1].i ];
            if ( g_sa[i-1] < g_sa[i] )
            {
                rank[ g_sa[i].i ]++;
            }
        }
        for (int i = 0; i < len; i++)
        {
            g_sa[i].i = i;
            g_sa[i].key[0] = rank[i];
            g_sa[i].key[1] = i + wid < len? rank[i + wid]: 0;
        }
        cSort(g_sa, len, 1, temp);
        cSort(temp, len, 0, g_sa);
        wid *= 2;
    }
}

int getLCP(char* a, char* b)
{
    int l=0;
    while(*a && *b && *a==*b)
    {
        l++;
        a++;
        b++;
    }
    return l;
}

void getLCP(char* text, Sfx* sfx, int len, int* lcp)
{
    int* rank = g_buf;
    for (int i=0, r=0; i < len; i++, r++)
    {
        rank[ sfx[i].i ] = r;
    }
    lcp[0] = 0;
    if (rank[0])
    {
        lcp[ rank[0] ] = getLCP( text, text + sfx[ rank[0]-1 ].i );
    }
    for (int i = 1; i < len; i++)
    {
        if ( !rank[i] )
        {
            continue;
        }
        if (lcp[ rank[i - 1] ] <= 1)
        {
            lcp[ rank[i] ] = getLCP( text+i, text+sfx[ rank[i]-1 ].i );
        }
        else
        {
            int L = lcp[ rank[i - 1] ] - 1;
            lcp[rank[i]] = L+getLCP(text+i+L, text+sfx[rank[i]-1].i+L);
        }
    }
}
int rank[100010];
char str[100010];

int main()
{
    while(~scanf("%s",str))
    {
        int lcp[100010];
        int len=strlen(str);
        buildSA(str,len);    //获取sa数组
        getLCP(str,g_sa,len, lcp); //获取height[]即lcp[]  g_buf即 rank[];
        build(1,0,len-1);
        for(int i=0; i<len; i++)  //线段树查找
           insert(1,i,lcp[i]);
        int m;
        scanf("%d",&m);
        int x,y,lx,ly;
        scanf("%d%d",&x,&y);
        lx=x;ly=y;
        long long ans1=0,ans2=y-x+3;  //需要long long
        ans1+=(y-x+1);
        for(int i=1; i<m; i++)
        {
            scanf("%d%d",&x,&y);
            ans1+=(y-x+1);
            int sum=0,sum1=0;
            if(x==lx)
            {
                if(y<=ly)
                    sum=y-x;
                else
                    sum=ly-lx;
            }
            else
            {
                if(g_buf[x]>g_buf[lx])  //比较rank的大小  决定起始位置
                    sum=findmin(1,g_buf[lx]+1,g_buf[x]);
                else
                    sum=findmin(1,g_buf[x]+1,g_buf[lx]);
                sum=min(sum,min(y-x,ly-lx));//三个长度的最小
            }
            ans2-=sum;
            if(sum==0)  //注意等于0时也是1位
                sum1++;
            else
            {
                while(sum)
                {
                    sum/=10;
                    sum1++;
                }
            }
            ans2=ans2+sum1+y-x+2;
            ly=y; lx=x;
        }
        printf("%I64d %I64d\n",ans1,ans2);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值