1. 程式人生 > >LCP poj 2217 尋找最長公共子串

LCP poj 2217 尋找最長公共子串

題目:http://poj.org/problem?id=2217

首先解釋,DP中的最長公共子序列和此處的最長公共子串區別-------------------序列可以是不連續的,但是子串是連續的

其次,LCP,lcp[i]就是lcp[rank[i]]和lcp[rank[i]+1]的最長公共字首,那麼把兩個字串接起來,然後找最長的lcp,就是答案

思路還是比較清晰的

上程式碼:

/*******************************************************/
//poj 2217 lcp+sa by Pilgrim
//最長公共子串---注意與動態規劃的最長公共子序列不同
//2014.4.2
/******************************************************/

#include <string>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <vector>

#define MAXN 10010
#define INF 0x80000000
//0x7fffffff
//0x80000000

using namespace std;

int n,k;//n=strlen(s);

int Rank[MAXN];
int tmp[MAXN];

/*使用Rank對sa排序*/
bool cmpSa(int i, int j)
{
    if(Rank[i] != Rank[j])return Rank[i] < Rank[j];
    else
    {   /*下面的Rank[t],已經是以t開頭長度小於等於k/2的,
        sa[i]的名次,只是以i開頭的字尾,而長度不同*/
        int ri = i+k <=n? Rank[i+k]:-1;
        int rj = j+k <= n ? Rank[j+k]:-1;
        return ri <rj;
    }
}

/*計算SA*/
void con_sa(char *s, int *sa)
{
    /*n=strlen(s);  必要時註明*/
    /*初始化sa和rank保證兩點
        1、Rank[i]表示下標為i的是第幾大,必須表示出相對大小,可以直接用字元代表其大小
        2、sa[1...n]值為1..n*/
    for(int i=0;i<=n;i++){
        sa[i]=i;Rank[i] = i < n?s[i]:-1;
    }

    /*利用長度為k的字串對長度為2*k的字串排序*/
    for(k=1;k<=n;k*=2)/*注意此程式碼中k是全域性變數 別亂用,迴圈必須從1開始,因為0*2=0*/
    {
        sort(sa,sa+n+1,cmpSa);
        tmp[sa[0]] = 0; /*此時tmp只是暫存rank*/
        for(int i=1;i<=n;i++){
            tmp[sa[i]] = tmp[sa[i-1]] +(cmpSa(sa[i-1],sa[i])?1:0);
            /*這一句很關鍵,等號右側的sa[i]在此迴圈裡表示第i大的長度小於等於k/2的字串,
              從而求出第i大的長度小於等於k的字串的sa[i]*/
        }
        for(int i=0;i<=n;i++){
            Rank[i] = tmp[i];
        }
    }
}

void construct_lcp(char *s,int *sa,int *lcp)
{
    //n=strlen(s);
    for(int i=0; i<=n; i++)Rank[sa[i]]=i;

    int h=0;
    lcp[0]=0;
    for(int i=0;i<n;i++)
    {
        int j=sa[Rank[i]-1];

        if(h>0)h--;
        for(; j+h<n && i+h<n; h++)
        {
            if(s[j+h]!=s[i+h])break;
        }
        lcp[Rank[i]-1]=h;
    }
}


int main()
{
    int sa[MAXN],lcp[MAXN];
    char s[MAXN],t[MAXN];
    char c;
    int ncase,mmax,len,leng;

    while(scanf("%d",&ncase)!=EOF)
    {
        while(ncase--)
        {
            mmax =0;
            while((c=getchar())==' '|| c=='\n');
            s[0]=c;
            int tt=1;
            while((c=getchar()) != '\n')
            {
                s[tt++]=c;

            }

            s[tt]='\0';
            while((c=getchar())==' '|| c=='\n');
            t[0]=c;
            tt=1;
            while((c=getchar()) != '\n')
            {
                 t[tt++]=c;

            }
            t[tt]='\0';
            len=strlen(s);
            leng =len+1+strlen(t);
            strcpy(s+len+1,t);
///////////////////////////////////////////////
//for(int i=0;i<leng;i++)
 //   putchar(s[i]);
//putchar('\n');
            n=leng;
            con_sa(s,sa);
            construct_lcp(s,sa,lcp);

           for(int i=0;i<leng;i++)
            {
              if((sa[i]<len) != (sa[i+1]<len))
                  mmax=max(mmax,lcp[i]);
            }
            printf("Nejdelsi spolecny retezec ma delku %d.\n",mmax);
        }
    }

    return 0;
}