LCS最長公共子串

問題介紹

LCS問題(longest common subsequence problem)指的是求解兩個字符串最長公共子序列問題。這里的子序列是可以不連續的。LCS問題廣泛地出現在計算生物學中(DNA序列、系統生成樹等等)。這里介紹如何解決LCS問題,以及算法的正確性證明和性能分析。

解決方案

假設需要求解串X,Y的LCS,其中|X|=n,|Y|=m,c[i][j]表示X[1…i]和Y[1…j]的LCS長度,z[1…k]表示X[1…i]和Y[1…j]的LCS,k=c[i][j],則問題就是求解c[n][m]和z[c[n][m]]c[n][m]和z[c[n][m]]c[n][m]z[c[n][m]]

樸素的想法是,按照問題的要求,我們可以得到串X的所有子串,共有2n2^n2n個,然后判斷該子串是否出現在串Y中,每次判斷都需要遍歷串Y,因此時間復雜度為O(2n?m)O(2^n*m)O(2n?m),這顯然是我們不能接受的復雜度。

為了解決這個問題,我們需要得到該問題的一些性質:

定理:

ifX[i]==Y[j]:c[i][j]=c[i?1][j?1]+1if X[i]==Y[j]:c[i][j]=c[i-1][j-1]+1 ifX[i]==Y[j]:c[i][j]=c[i?1][j?1]+1

otherwise:c[i][j]=max(c[i?1][j],c[i][j?1])otherwise:c[i][j]=max(c[i-1][j],c[i][j-1]) otherwisec[i][j]=max(c[i?1][j],c[i][j?1])

引理1:如果X[i]==Y[j],則z[c[i][j]]=X[i]z[c[i][j]]=X[i]z[c[i][j]]=X[i]

證明:如果z[c[i][j]]≠X[i]z[c[i][j]]\neq X[i]z[c[i][j]]?=X[i],且X[i]==Y[j],那么不妨將X[i]加入到LCS中,c[i][j]c[i][j]c[i][j]加一,因此z[1..c[i][j]]z[1..c[i][j]]z[1..c[i][j]]不是LCS,與條件矛盾,證畢。

引理2:如果X[i]==Y[j],則X[i-1]和Y[j-1]的LCS是z[1..c[i][j]?1]z[1..c[i][j]-1]z[1..c[i][j]?1]

證明:X[i-1]和Y[j-1]的LCS不是z[1..c[i][j]?1]z[1..c[i][j]-1]z[1..c[i][j]?1],則使用X[i-1]和Y[j-1]的LCS替換z[1..c[i][j]?1]z[1..c[i][j]-1]z[1..c[i][j]?1]后再加上X[i]會得到X[i]和Y[j]的一個更長的一個LCS,與條件矛盾,證畢。引理2這里展示了問題的最優子結構

引理3:如果兩個串的LCS包含兩個串的末尾元素X[i]和Y[j],則這兩個元素相等

證明:如果X[i]不等于Y[j],則在LCS中,X[i]對應Y[j’],j’<j,Y[j]對應X[i’],i’<i,這不符合公共子串保留原串順序的性質,矛盾。證畢。

當X[i]==Y[j]時,由引理1保證了此時的LCS是串X[i]對應串Y[j’],j′?jj'\leqslant jj?j,對于j′<jj' < jj<j的情況,我們不妨用jjj來替換j′j'j,這樣也不會對LCS的長度有什么影響,然后由引理2,c[i][j]=c[i?1][j?1]+1c[i][j]=c[i-1][j-1]+1c[i][j]=c[i?1][j?1]+1

對于第二個條件,因為X[i]≠\neq?=Y[j],那么此時的LCS要么屬于c[i?1][j]c[i-1][j]c[i?1][j],要么屬于c[i][j?1]c[i][j-1]c[i][j?1]

假設都不屬于,那么此時的LCS一定包含了c[i?1][j]c[i-1][j]c[i?1][j]中沒有的元素X[i]和c[i][j?1]c[i][j-1]c[i][j?1]中沒有的元素Y[j],由引理3,矛盾。

在證明了上述定理以后我們可以根據該式子計算LCS:

  1. 遞歸法
int LCS(string& x, string &y,int n,int m)
{if(-1==n || -1==m) return 0;if(x[n]==y[m]) return LCS(x, y, n-1, m-1)+1;else return max(LCS(x, y, n-1, m), LCS(x, y, n, m-1));
}

性能分析

分析遞歸樹,最壞的情況是每次x[n]!=y[m],那么會得到一個高度為n+m的二叉樹,時間復雜度為O(2n+m)O(2^{n+m})O(2n+m),空間復雜度為O(n+m)O(n+m)O(n+m)

  1. 備忘錄方法(記憶化搜索)

分析上面時間復雜度我們發現,在搜索過程中很多的子問題都是一模一樣的,也就是具有重疊子問題性質,因此我們不妨每計算出一個子問題的結果就進行一次記錄,后面再次需要求解結果的時候就不需要再計算,而是直接返回結果。

int LCS(string& x, string &y,int n,int m,int *c)
{if(-1==n || -1==m) return 0;if(c[n*y.size()+m]) return c[n*y.size()+m];int ret;if(x[n]==y[m]) ret = LCS(x, y, n-1, m-1, c)+1;else ret = max(LCS(x, y, n-1, m, c), LCS(x, y, n, m-1, c));return c[n*y.size()+m]=ret;
}int main()
{string X,Y;cout<<"請輸入字符串X:"; cin>>X;cout<<"請輸入字符串Y:"; cin>>Y;int* c = new int[X.size()*Y.size()+10]();cout<<"字符串X和Y的LCS的長度為"<<LCS(X, Y, X.size(), Y.size(), c)<<endl;delete c;return 0;
}

性能分析

我們可以把對結果是否已經計算出的判斷和返回答案的耗費記錄在調用該狀態答案的耗費上,把實際結果的計算記錄在該狀態中,則最壞情況下每種狀態都要計算出來,因此時間復雜度為O(nm)O(nm)O(nm),空間復雜度為O(nm)O(nm)O(nm)

  1. 自底向上計算(動態規劃法)

我們觀察計算的過程,如果我們對狀態空間按照從左向右從上向下進行求解,就可以計算出所有的答案

int LCS(string& x, string &y,int n,int m,int *c)
{for(int i=1; i<=x.size(); ++i){for(int j=1;j<=y.size(); ++j){if(x[i-1] == y[j-1])c[i*y.size()+j]=c[(i-1)*y.size()+j-1]+1;elsec[i*y.size()+j]=max(c[(i-1)*y.size()+j], c[(i)*y.size()+j-1]);}}return c[n*y.size()+m];
}

性能分析

時間復雜度O(nm)O(nm)O(nm),因為訪問的是連續的內存空間,因此這里的O(nm)O(nm)O(nm)應該比上面小。空間復雜度O(nm)O(nm)O(nm),如果使用滾動數組還能夠將空間復雜度降低為O(min(n,m))O(min(n,m))O(min(n,m))如果不使用滾動數組,想要得到完整的LCS串需要在計算的時候設置指針,最后進行回溯。如果使用滾動數組,需要使用分治法得到LCS串

回溯如圖:
在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383642.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383642.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383642.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

將字符串中的空格用%20替換

如果不需要原地操作&#xff0c;則一遍遍歷&#xff0c;將非空串復制&#xff0c;遇到空格加上%20&#xff0c;如果需要原地操作&#xff0c;首先進行遍歷出空格的個數x,然后擴容2x,從后往前遍歷實現。如果非空格字符串比空格字符串多的多的時候而且字符串非常長的時候使用原地…

12步輕松搞定python裝飾器

http://python.jobbole.com/81683/ 呵呵&#xff01;作為一名教python的老師&#xff0c;我發現學生們基本上一開始很難搞定python的裝飾器&#xff0c;也許因為裝飾器確實很難懂。搞定裝飾器需要你了解一些函數式編程的概念&#xff0c;當然還有理解在python中定義和調用函數…

操作系統【六】虛擬內存

傳統存儲管理方式的不足 一次性&#xff1a;作業必須一次性全部裝入內存后才能開始運行。這會造成&#xff1a;當作也很大時不能全部裝入內存&#xff1b;當大量作業要求運行時&#xff0c;由于內存無法容納所有作業&#xff0c;因此只有少量作業能夠運行&#xff0c;導致多道…

python裝飾器詳解

https://blog.csdn.net/xiangxianghehe/article/details/77170585 你會Python嘛&#xff1f; 我會&#xff01; 那你給我講下Python裝飾器吧&#xff01; Python裝飾器啊&#xff1f;我沒用過哎 以上是我一個哥們面試時候發生的真實對白。 ———————————————-分…

SQL Server【一】簡介和基本概念和命令

數據結構和數據庫的區別 數據庫是應用軟件級別研究數據的存儲和操作&#xff08;主要針對磁盤上的數據&#xff09; 數據結構是在系統軟件級別研究數據的存儲和操作&#xff08;主要是針對內存中的數據&#xff09; 對硬盤數操作是數據庫的強項&#xff0c;是數據庫研究的核心…

SQL Server【二】單表查詢

查詢 計算列 select * from emp; -- *通配符&#xff0c;表示所有的字段 -- from emp 從emp表查詢select empno, ename from emp; select ename as "員工姓名", sal*12 as "年薪" from emp;-- as可以省略&#xff0c;用于設置字段名 -- 注意用雙引號將字…

SQL Server【三】連接查詢

將兩個表或者兩個以上的表以一定的連接條件連接起來&#xff0c;從中檢索出滿足條件的數據。 內連接 使用inner join&#xff0c;inner可以省略 -- 查詢員工的姓名和部門名稱 select "E".ename as "員工姓名", "D".dname as "部門名稱&q…

Linux下網絡socket編程——實現服務器(select)與多個客戶端通信

一、關于socket通信 服務器端工作流程&#xff1a; 調用 socket() 函數創建套接字 用 bind() 函數將創建的套接字與服務端IP地址綁定調用listen()函數監聽socket() 函數創建的套接字&#xff0c;等待客戶端連接 當客戶端請求到來之后調用 accept()函數接受連接請求&#xff0c…

SQL Server【四】

identity 主鍵自動增長&#xff0c;用戶不需要為identity修飾的主鍵賦值 create table student (std_id int primary key identity(10,5),--(10,5)可以省略&#xff0c;默認為(1,1)std_name nvarchar(200) not null ) select * from student insert into student values (張三…

TCP服務器/客戶端實例(C/C )

1.1、Linux下的TCP服務器&#xff1a; #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/socket.h>void error_handling(char *mess…

pip代理解決pip下載失敗問題

在用pip下載各種庫的時候發現速度實在是太慢了&#xff0c;還會有各種奇奇怪怪的問題&#xff0c;動不動就玄學失敗。 在網上找來找去找到知乎上一位大佬的回答&#xff1a;傳送門&#xff0c;用了豆瓣的代理。哇咔咔&#xff0c;媽媽再也不用擔心我下載失敗了。 代理&#x…

實現Linux select IO復用C/S服務器代碼

服務器端#include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<string.h> #include<sys/socket.h> #include<sys/stat.h> #include<arpa/inet.h> #include <sys/select.h>#define MAXBUF 256 #define MAXLISTEN…

Bellman-Ford算法和SPFA算法

Belloman-Ford算法 算法介紹 Dijkstra可以解決單源無負邊最短路徑問題。但是當遇到含有負邊的單源最短路徑問題就需要使用Bellman-Ford算法來解決。Bellman-Ford算法還可以檢測出負環。 算法步驟 源點s,數組d[u]d[u]d[u]表示s到u的最短距離初始化&#xff1a;d[s]0d[s]0d[s…

C語言實現單鏈表操作

SLIST_H #ifndef __SLIST_H__ #define __SLIST_H__ #include<cstdio> #include<malloc.h> #include<assert.h> typedef int ElemType; typedef struct Node { //定義單鏈表中的結點信息 ElemType data; //結點的數據域 struct Node *next; //結點的指針…

計算機網絡【4】傳輸層

概述 傳輸層是只有主機才有的層次 傳輸層的功能&#xff1a; 傳輸層提供進程和進程之間的邏輯通信&#xff08;網絡層提供主機與主機之間的邏輯通信&#xff09;復用和分用傳輸層對收到的報文進行差錯檢測 傳輸層有兩個協議&#xff1a; 面向連接的傳輸層控制協議TCP&…

Plotly繪圖

在做Python數據分析實驗的時候發現使用Plotly庫繪圖比較漂亮&#xff0c;在網上找到了一個比較好的教程&#xff0c;這里記錄一下&#xff0c;方便以后查找。 傳送門

計算機網絡【0】概述

計算機網絡概念和功能 概念 是一個將分散的、具有獨立功能的計算機系統&#xff0c;通過通信設備與線路連接起來&#xff0c;由功能完善的軟件實現資源共享和信息傳遞的系統。 計算機網絡是互連的、自治&#xff08;無主從關系&#xff09;的計算機集合。 功能 數據通信&am…

計算機網絡【1】物理層

物理層解決如何在連接各種計算機的傳輸媒體上傳輸數據比特流&#xff0c;而不是指具體的傳輸媒體。 確定與傳輸媒體接口有關的特性 機械特性&#xff1a;定義物理連接的特性&#xff0c;如規格、接口形狀、引線數目、引腳數目、排列電氣特性&#xff1a;規定傳輸二進制位時的電…

計算機網路【2】數據鏈路層

結點&#xff1a;主機、路由器 鏈路&#xff1a;兩個節點的物理通道 數據鏈路&#xff1a;邏輯通道&#xff0c;把實現 控制數據傳輸協議的硬件和軟件加到鏈路上就構成數據鏈路 幀&#xff1a;鏈路層的協議數據單元&#xff0c;封裝網絡層數據報 數據鏈路層在物理層提供服務的…

計算機網絡【5】應用層

應用層對應用程序的通信提供服務 應用層協議定義&#xff1a; 應用層的功能&#xff1a; 文件傳輸、訪問和管理電子郵件虛擬終端查詢服務和遠程作業登錄 重要協議&#xff1a;FTP、SMTP、POP3、HTTP、DNS 網絡應用模型 客戶/服務器模型&#xff08;Client/Server&#x…