后缀数组 - poj2774 Long Long Message

题目:

http://poj.org/problem?id=2774


题意:

给两个字符串,求最大公共子串的长度


思路:

后缀数组模板题,我是拿这题来入门后缀数组的

在两个字符串中插入一个比两个字符串中任一字符小的字符,在本题中即插入一个('a'-1),然后合并两个字符串,在其他题解中会看到,当合并多个字符串时,要在每个间隔都加上一个不同的逼所有字符串中任一字符小的字符。然后对合并完的字符串求后缀数组,因为间隔字符的存在,height数组中记录的lcp值不会越过第一个字符串到达第二个,这一点很重要,一定要理解为什么要插入不同的字符,而保证插入字符小于串中任意字符是为了保证,每个字符串的后缀数组能够与独立情况下求得的后缀数组一致,因为在独立情况下,字符串结尾字符为'\0',即默认结尾字符小于任一字符。之后遍历height数组,找到lcp最大,且两个后缀串分别位于第一个和第二个字符串的后缀即可


代码:

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>

#define rep(i,n) for(int i = 0;i < n; i++)
using namespace std;

const int MAXSIZE  = 2*1e6 + 100;
int rk[MAXSIZE],sa[MAXSIZE],height[MAXSIZE],wa[MAXSIZE],res[MAXSIZE];
char w[MAXSIZE];
int len;

void getSa (int up) {
    int *k = rk,*id = height,*r = res, *cnt = wa;
    rep(i,up) cnt[i] = 0;
    rep(i,len) cnt[k[i] = w[i]]++;
    rep(i,up) cnt[i+1] += cnt[i];
    for(int i = len - 1; i >= 0; i--) {
        sa[--cnt[k[i]]] = i;
    }
    int d = 1,p = 0;
    while(p < len){
        for(int i = len - d; i < len; i++) id[p++] = i;
        rep(i,len)  if(sa[i] >= d) id[p++] = sa[i] - d;
        rep(i,len) r[i] = k[id[i]];
        rep(i,up) cnt[i] = 0;
        rep(i,len) cnt[r[i]]++;
        rep(i,up) cnt[i+1] += cnt[i];
        for(int i = len - 1; i >= 0; i--) {
            sa[--cnt[r[i]]] = id[i];
        }
        swap(k,r);
        p = 0;
        k[sa[0]] = p++;
        rep(i,len-1) {
            if(sa[i]+d < len && sa[i+1]+d < len && r[sa[i]] == r[sa[i+1]] && r[sa[i]+d] == r[sa[i+1]+d])
                k[sa[i+1]] = p - 1;
            else k[sa[i+1]] = p++;
        }
        if(p >= len) return ;
        d <<= 1,up = p, p = 0;
    }
}
void getHeight() {
    int i,k,h=0;
    rep(i, len) rk[sa[i]]=i;
    rep(i, len){
        if (rk[i]==0)
            h=0;
        else
        {
            k=sa[rk[i]-1];
            if (h) h--;
            while (w[i+h] == w[k+h]) h++;
        }
        height[rk[i]]=h;
    }
}

int l;
char s[MAXSIZE], t[MAXSIZE];

void getSuffix(char s[]) {
    l = strlen(s);
    s[l] = 'a' - 1;
    s[l+1] = 0;
    strcat(s,t);
    //cout<<s<<endl;
    len = strlen(s);
    int up = 0;
    for(int i = 0; i < len; i++) {
        w[i] = s[i] - 'a' + 2;
        up = up > w[i] ? up : w[i];
    }
    w[len+1] = 0;
    getSa(up+1);
    getHeight();
}

int main(){
    scanf("%s",s);
    scanf("%s",t);

    getSuffix(s);
    int maxinum = 0;
    for (int i=1;i<len;++i){
        if (height[i]>maxinum)
            if ((sa[i-1]<l && sa[i]>l) || (sa[i-1]>l && sa[i]<l))
                maxinum = height[i];
    }
    cout<<maxinum<<endl;
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值