【求最长公共前缀 后缀数组】hdu1403

【求最长公共前缀 后缀数组】hdu1403

【vj链接】


题目内容

给两个字符串,求最长公共子串,多组数据,长度1e5

解题思路

利用后缀数组的性质,把两个串连起来,找最大的满足公共串部分分别在两个字符串里的height。


AC代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
const int maxn = 2e5+7;
using namespace std;
///比较两个串是否相同
///分别比较两个关键字
bool same(int *rank, int idx1, int idx2, int len){
    return rank[idx1]==rank[idx2] && rank[idx1+len]==rank[idx2+len];
}

/// 输入字符串的末尾要补一个 '0' , n是字符串的实际长度+1.

char s1[maxn];
int s[maxn];
int sa[maxn],sa2[maxn],rk[maxn],cnt[maxn],hgt[maxn];///rk[i]是i的排名,sa[i]是第i小的开头
void SA(int s[], int n, int m){///n是字符串长度+1,m是基数排序的基数

    for(int i=0; i<m; i++) cnt[i]=0;
    for(int i=0; i<n; i++) cnt[rk[i]=s[i]]++;
    for(int i=1; i<m; i++) cnt[i]+=cnt[i-1];
    for(int i=n-1; i>=0; i--) sa[--cnt[rk[i]]]=i;

    for(int len=1; len<n; len*=2){
        ///step-1: 计算(填充) sa2[]
        ///sa2[]: 按第二关键字对后缀排序的结果
        ///对后缀按第二关键字排序这一步可直接用上一次的sa[]数组, 这个过程可形象地理解为将后缀按顺序从一个数组中拿出来放到另一个数组中
        int p=0;
        for(int i=n-len; i<n; i++) sa2[p++]=i;   //第二关键字为空的后缀放在最开头
        ///接着放第二关键字非空的
        ///形象化地理解: 将后缀从一个数组里按顺序拿出来放到另一个数组里
        for(int i=0; i<n; i++)
            if(sa[i]>=len)
                sa2[p++]=sa[i]-len;

        ///step-2 fill sa[], countig sort
        ///计数排序: 每个后缀的第一关键字 (first key) 为上一次针对长度减半的 (部分) 后缀求出来的rank
        for(int i=0; i<m; i++) cnt[i]=0;
        for(int i=0; i<n; i++) cnt[rk[i]]++;
        for(int i=1; i<m; i++) cnt[i]+=cnt[i-1];
        for(int i=n-1; i>=0; i--)
            sa[--cnt[rk[sa2[i]]]]=sa2[i];
        ///step-1 and step-2 together constitute a radix sort

        ///fill rk[]
        swap(rk, sa2);
        rk[sa[0]]=0;
        for(int i=1; i<n; i++)
            ///这里, sa2指向的是旧rank数组
            rk[sa[i]]=rk[sa[i-1]]+!same(sa2, sa[i-1], sa[i], len);

        m=rk[sa[n-1]]+1;
        if(m==n) break;
    }

    ///hgt[i]表示第i小和第i-1小的最长公共前缀
    ///h[i]表示i开头的后缀和它的上一名的最长公共前缀,h[i]=hgt[rk[i]];
    for(int i=0, j, lcp=0; i<n-1; i++){
        lcp?--lcp:0;
        /// 因为在最后加了一个0,最小的一定是最后一个,所以rk[i]>0
        j=sa[rk[i]-1];
        for(; s[j+lcp]==s[i+lcp]; lcp++);
        hgt[rk[i]]=lcp;
    }
}

int main()
{
    int len,i,j,k,len1,l,r,x,y;
    while(scanf("%s",s1)!=EOF)
    {
        len=len1=strlen(s1);
        scanf("%s",s1+len);
        len=strlen(s1);
        for(i=0;i<len;i++)
        {
            s[i]=s1[i]-'a'+1;
        }
        s[len++]=0;
        SA(s,len,27);
        int ans=0;
        for(i=1;i<len;i++)
        {
            l=sa[i-1],r=sa[i];
            if(l>r) swap(l,r);
            x=hgt[i];
            if(l<len1&&r>len1)
            {
                if(l+x>=len1) x=len1-l-1;
                if(x>ans) ans=x;
            }
        }
        printf("%d\n",ans);
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值