1. 程式人生 > >【mark】最長公共子序列(poj 1458+hdu 1159)

【mark】最長公共子序列(poj 1458+hdu 1159)

經典的問題,在各大部落格上有數不清的好帖子

下面為最常見的n^2演算法

recursive formula

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
int f[500][500];
char x[500],y[500];
int solve(int a,int b)
{
  int i,j;
  memset(f,0,sizeof(f));
  for(i=1;i<=a;i++)
  {
     for(j=1;j<=b;j++)
     {
        if(x[i]==y[j])
        {
            f[i][j]=f[i-1][j-1]+1;
        }
        else
        f[i][j]=max(f[i-1][j],f[i][j-1]);
     }
  }
  return f[a][b];
}
int main()
{
    int a,b;
    while(scanf("%s",&x[1])!=EOF)
    {
        scanf("%s",&y[1]);
        a=strlen(&x[1]);
        b=strlen(&y[1]);
        printf("%d\n",solve(a,b));

    }
    return 0;
}

O(nlogn):

最長公共子序列(LCS)最常見的演算法是時間複雜度為O(n^2)的動態規劃(DP)演算法,但在James W. Hunt和Thomas G. Szymansky 的論文"A Fast Algorithm for Computing Longest Common Subsequence"中,給出了O(nlogn)下限的一種演算法。

定理:設序列A長度為n,{A(i)},序列B長度為m,{B(i)},考慮A中所有元素在B中的序號,即A某元素在B的序號為{Pk1,Pk2,..},將這些序號按照降序排列,然後按照A中的順序得到一個新序列,此新序列的最長嚴格遞增子序列即對應為A、B的最長公共子序列。

舉例來說,A={a,b,c,d,b},B={b,c,a,b},則a對應在B的序號為2,b對應序號為{4,0},c對應序號為1,d對應為空集,生成的新序列為{2,4, 0, 1, 4, 0},其最長嚴格遞增子序列為{0,1,4},對應的公共子序列為{b, c, b}

原論文的證明過程較複雜,其實可以簡單的通過一一對應來證明。即證明A、B的一個公共子序列和新序列的一個嚴格遞增子序列一一對應。

(1) A、B的一個公共子序列對應新序列的一個嚴格遞增子序列

假設A、B的某一個公共子序列長度為k,則其公共子序列在A和B中可以寫為

{Ai1,Ai2, ..., Aik}

{Bj1,Bj2, ..., Bjk

}

如此有Ai1 = Aj1,Ai2 = Aj2, ...., Aik = Ajk, 考慮元素Bj1在B中的序號P(Bj1),則有

P(Bj1)< P(Bj2) < ... < P(Bjk)

注意此嚴格遞增子序列屬於新序列的一個子序列,因此得證

(2) 新序列的一個嚴格遞增子序列對應A、B的一個公共子序列

設新序列的一個嚴格遞增子序列{P1,P2, ..., Pk},任意兩個相信的P不可能屬於A中同一個元素,因為A中某元素在B中的序號按照降序排列,但此序列為嚴格遞增序列,矛盾。所以每個P均對應於A中不同位置的元素,設為{Ai1, Ai2, ..., Aik}。

因為P是嚴格遞增序列,則每個P也對應B中唯一的一個元素,假設為{Bj1,Bj2, ..., Bjk},由P的定義可知Ai1= Aj1, Ai2 = Aj2, ...., Aik = Ajk,因此得證。

實現上比較複雜,有以下幾個步驟:

(1) 對序列B排序

(2) 計算A中每個元素在B中的序號,並構成新序列

(3) 使用LIS的方法計算最長嚴格遞增子序列

(4) 獲取最長公共子序列

效能分析:

(1) 排序複雜度為nlogn

(2) 獲取一個元素在B中的序號的複雜度,最小為logn,最大為n,獲取所有元素的複雜度為 nlogn === n*n

(3) LIS 複雜度為nlogn

因此總體複雜度在nlogn 到 n*n logn之間,但如果(2) 步驟中A中元素在B中的序號對數很少時,效能相當優越,在實際測試時,string 中均為小寫字母,長度為10000的情況下,這種方法比普通的LCS快一倍以上;如果string 中的字元擴充套件成char,即0-255,則這種方法比普通的LCS快至少一個數量級。以下為程式碼實現,可以參考:

附個參考程式碼:

  1. #include <stdio.h>  
  2. #include <ctype.h>  
  3. #include <string.h>  
  4. #include <iostream>  
  5. #include <string>  
  6. #include <math.h>  
  7. #include <vector>  
  8. #include <queue>  
  9. #include <algorithm>  
  10. using namespace std;  
  11. const int maxn = 1501 ;  
  12. vector<int> location[26] ;  
  13. int c[maxn*maxn] , d[maxn*maxn] ;  
  14. inline int get_max(int a,int b) {   return a > b ? a : b ;  }  
  15. //nlogn 求lcs  
  16. int lcs(char a[],char b[])  
  17. {  
  18.     int i , j , k , w , ans , l , r , mid ;  
  19.     for( i = 0 ; i < 26 ; i++) location[i].clear() ;  
  20.     for( i = strlen(b)-1 ; i >= 0 ; i--) location[b[i]-'a'].push_back(i) ;  
  21.     for( i = k = 0 ; a[i] ; i++)  
  22.     {  
  23.         for( j = 0 ; j < location[w=a[i]-'a'].size() ; j++,k++) c[k] = location[w][j] ;  
  24.     }  
  25.     d[1] = c[0] ;   d[0] = -1 ;  
  26.     for( i = ans = 1 ; i < k ; i++)  
  27.     {  
  28.         l = 0 ; r = ans ;  
  29.         while( l <= r )  
  30.         {  
  31.             mid = ( l + r ) >> 1 ;  
  32.             if( d[mid] >= c[i] ) r = mid - 1 ;  
  33.             else l = mid + 1 ;  
  34.         }  
  35.         if( r == ans ) ans++,d[r+1] = c[i] ;  
  36.         else if( d[r+1] > c[i] ) d[r+1] = c[i] ;  
  37.     }  
  38.     return ans ;  
  39. }  
  40. int main()  
  41. {  
  42.     char a[maxn] , b[maxn] ;  
  43.     while (~scanf("%s%s",a,b))  
  44.     {  
  45.         printf("%d/n",lcs(a,b));  
  46.     }  

本文出自 “[email protected]” 部落格,請務必保留此出處http://karsbin.blog.51cto.com/1156716/966387


相關題目:

POJ1458
POJ2250
POJ1159
基本的最長公共子序列

POJ2533
最長不減(不增)子序列,用方法1、2即可

WOJ1398
最長不減(不增)子序列,方法1、2超時


相關推薦

mark公共序列poj 1458+hdu 1159

經典的問題,在各大部落格上有數不清的好帖子 下面為最常見的n^2演算法 #include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #inclu

DP公共序列

amp 給定 scrip ros script print 最長 去掉 != Description   字符序列的子序列是指從給定字符序列中隨意地(不一定連續)去掉若幹個字符(可能一個也不去掉)後所形成的字符序列。令給定的字符序列X=“x0,x1,…,xm-1”,序列Y

洛谷 P1439 模板公共序列

clu () 休閑 一句話 中一 AD DC == c++ 神TM模板。。我本來想休閑一下寫點水題的。。。 開始做的時候直接敲了一個O(N2)的算法上去,編譯的時候才發現根本開不下。。 好了,談回這道題。 先不加證明的給出一種算法。 若有一組數據 2 4 2 5 1 3

luogu P1439 模板公共序列

bsp 第一個 順序 它的 amp while ++ return scan 題目qwq (第一道藍題) 據說是用了hash的思想(?) 總之是先把第一個序列每個數出現的順序記下來(其實第一個序列的數字不用記), 然後第二個序列的每個數都對照它的順序,這樣只要得到一個升序的

P1439 模板公共序列

題目描述 給出1-n的兩個排列P1和P2,求它們的最長公共子序列。 輸入輸出格式 輸入格式: 第一行是一個數n, 接下來兩行,每行為n個數,為自然數1-n的一個排列。 輸出格式: 一個數,即最長公共子序列的長度 輸入輸出樣例 輸入樣例#1: 複製 5 3 2 1 4 5 1 2 3 4 5

洛谷P1439 模板公共序列

1-n ont range else 輸入輸出格式 pac algo mes 格式 題目描述 給出1-n的兩個排列P1和P2,求它們的最長公共子序列。 輸入輸出格式 輸入格式: 第一行是一個數n, 接下來兩行,每行為n個數,為自然數1-n的一個排列。 輸出格式:

模板公共序列(LCS)。

ons 理解 思路 esp 自動 const () -- bing 看過好多人的博客,感覺要麽是太復雜要麽就是太不容易理解。 那就親自動手寫一個通俗易懂的。 先定義兩個數組,第一個數組為主,用第二個數組來匹配第一個,看能有多少可以對應上的。 所以,其實第一個數組的內容可以暫

模板公共序列

#include <stdio.h> #include <string.h> #include <algorithm> using namespace

[題解]洛谷P1439 模板公共序列

序列 printf %d span pac namespace int turn esp 原題 原題 思路 將第一個序列依次從左到右標號,然後映射到第二個序列中 因為第一個序列標號只上升的所以問題就轉化為序列2標號後的最長上升子序列 代碼 #include&

洛谷 P1439 模板公共序列 題解

inf bubuko return https == span ++ 每一個 upper 題目傳送門 看起來是一道十分經典的LCS問題 很容易想到 n2 的一般算法:主題代碼如下: for (int i = 1; i <= n; i++) fo

bzoj5427上升序列貪心+LIS

貪心 去掉 ati lse com set event color tps   題目傳送門:https://www.lydsy.com/JudgeOnline/problem.php?id=5427   因為noip,博客咕了好久,這幾天集中填一下坑。   這題我們可以

NYOJ題目36-公共序列經典動態規劃題

最長公共子序列 時間限制:3000 ms  |  記憶體限制:65535 KB 難度:3 描述咱們就不拐彎抹角了,如題,需要你做的就是寫一個程式,得出最長公共子序列。 tip:最長公共子序

動態規劃公共序列問題

clas == 搜索 ios for 參考 pan 公式 是否 題目描述: 給定兩個字符串s1s2……sn和t1t2……tn。求出這兩個字符串最長的公共子序列的長度。字符串s1s2……sn的子序列指可以表示為si1si2……sim(i1<i2<……<im)

例9.9公共序列

std 不存在 cst i++ 公共子串 ostream ont enter http 【例9.9】最長公共子序列 鏈接:http://ybt.ssoier.cn:8088/problem_show.php?pid=1265 時間限制: 1000 ms 內

NOJ1041DP_動態規劃公共序列

1041.最長公共子序列 時限:1000ms 記憶體限制:200000K  總時限:3000ms 描述 一個給定序列的子序列是在該序列中刪去若干元素後得到的序列。確切地說,若給定序列X=<x1, x2,…, xm>,則另一序列Z=<z1, z2,

LeetCodeLongest Common Subsequence公共序列求出某一解+LCS長度

Longest Common Subsequence 給出兩個字串,找到最長公共子序列(LCS),返回LCS的長度。 說明 最長公共子序列的定義: • 最長公共子序列問題是在一組序列(通常2個)中找到最長公共子序列(注意:不同於子串,LCS不需要是

經典動態規劃問題公共序列LCS

目錄 題目 題目分析 狀態 邊界值討論 其他情況討論 程式碼實現 從題目出發分析如何用動態規劃求解最長公共子序列問題 題目 給定兩個字串A和B,返回兩個字串的最長公共子序列的長度。例如,A="1A2C3D4B56”,B="B1D23CA45B6A”,”1234

動態規劃公共序列公共

1. 問題描述 子串應該比較好理解,至於什麼是子序列,這裡給出一個例子:有兩個母串 cnblogs belong 比如序列bo, bg, lg在母串cnblogs與belong中都出現過並且出現順序與母串保持一致,我們將其稱為公共子序列。最長公共子序列(Longest Common Subsequence

水:公共序列HDU1159Common Subsequence

Common Subsequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 24919    Acc

演算法動態規劃法——公共序列LCS

前言       這篇是自己寫的第一篇關於演算法方面的部落格,寫他是因為自己今天開啟筆記,剛好看到了它,就這麼簡單。       這篇部落格主要想講講動態規劃法,然後以LCS問題為例展開來說一下怎麼利用動態規劃法求解它,下面是自己的一些理解和總結,有不對的地方還請大家指正。動