1. 程式人生 > >最長公共子序列針對小字符集的算法

最長公共子序列針對小字符集的算法

但是 公共子序列 ext 每一個 一個 post div span min

一般對於兩個字符串,長度分別為n和m,其時間復雜度為O(nm)。

但是針對小字符集的情況,可以把復雜度降低到O(n^2+km),其中n為兩個字符串較短的長度。這種方法對於兩個字符串長度相差很大的情況比O(nm)要優化很多。

就假設所有的字符都是小寫字母,這樣就符合小字符集的前提了。設較短的字符串為S1,較長的字符串為S2。字符串下標從1開始。

S2字符串每個位置右邊第一個字符是可以通過O(km)預處理得到的。其中k為小字符集的字符個數,m為較長的那個字符串的長度。

用next[i][j]表示S2[i]右邊第一個(char)(‘a‘+j)的位置。

設dp[i][j]表示S1匹配了前i位,長度為j的最長公共子序列與S2匹配到的最靠左的位置。如果不存在則為length(S2)+1。

dp[i][0] = 0

如果S2的dp[i-1][j-1]這個位置右邊第一個與S1[i]相等的位置存在,那麽dp[i][j] = min{ dp[i-1][j], next[dp[i-1][j-1]][S1[i]] }。

否則dp[i][j] = dp[i-1][j]。

對於每一個不是-1的dp,記錄下j,最後取一個最大就是最長公共子序列了。

因此,總的復雜度為O(n^2+km)。

具體實現:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int INF=0x3f3f3f3f;
const int maxn=1005;
const int maxm=1000005;
char s1[maxn],s2[maxm];
int dp[maxn][maxn];
int next[maxm][26]; 

int main()
{
    scanf("%s%s",s1+1,s2+1);  
    int l1=strlen(s1+1);
    int l2=strlen(s2+1);
    for (int i=0;i<maxm;i++)
        for (int j=0;j<26;j++)
            next[i][j]=l2+1;
    for (int i=0;i<maxn;i++)
        for (int j=0;j<maxn;j++)
            dp[i][j]=l2+1;
    for (int i=l2-1;i>=0;i--)
    {
        for (int j=0;j<26;j++)
        {
            char cc=‘a‘+j;
            if (s2[i+1]==cc) next[i][j]=i+1;
            else next[i][j]=next[i+1][j];
        }
    }
    for (int i=1;i<=l1;i++) dp[i][0]=0;
    int ans=0;
    for (int i=1;i<=l1;i++)
    {
        for (int j=1;j<=i;j++)
        {
            if (next[dp[i-1][j-1]][s1[i]-‘a‘]!=l2+1) dp[i][j]=min(dp[i-1][j],next[dp[i-1][j-1]][s1[i]-‘a‘]);
            else dp[i][j]=dp[i-1][j];
            if (dp[i][j]!=l2+1) ans=max(ans,j);
        }
    }
    printf("%d\n",ans);
    return 0;
}

最長公共子序列針對小字符集的算法