生活随笔
收集整理的這篇文章主要介紹了
动态规划算法---求最长公共子序列
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
最長公共子序列和最長公共子串區別
?????? 最長公共子串(Longest Common Substring)與最長公共子序列(Longest Common Subsequence)的區別: 子串要求在原字符串中是連續的,而子序列則只需保持相對順序一致,并不要求連續。例如X = {a, Q, 1, 1}; Y = {a, 1, 1, d, f}那么,{a, 1, 1}是X和Y的最長公共子序列,但不是它們的最長公共字串。
一、最長公共子序列
具體的算法思想參考以下文章:
?
http://blog.csdn.net/lisonglisonglisong/article/details/41548557
http://blog.csdn.net/zhongkeli/article/details/8847694
只求最長子序列長度
如果僅僅需要知道最長子序列的長度值,代碼如下:
?
[cpp]?view plain?copy
#include?<vector>??#include?<string>??#include?<iostream>??#include?<string.h>??#include?<sstream>??using?namespace?std;????//最長公共子串(LCS)??//二維數組veca記錄的是兩個字符串Xi和Yj的LCS長度??int?LCS_length(const?string?&str1,?const?string?&str2,?vector<vector<int>?>?&veca)?{??????int?i,?j;??????int?biggest?=?0;??????if?(str1?==?""?||?str2?==?"")??????????return?0;????????for?(i?=?0;?i?<=?str1.length();?i++)?{??? //初始化veca[][]????????veca[i][0]?=?0;??????}??????for?(j?=?0;?j?<=?str2.length();?j++)?{? ////初始化veca[][]????????veca[0][j]?=?0;??????}??????for?(i?=?1;?i?<=?str1.length();?i++)?{??????????for?(j?=?1;?j?<=?str2.length();?j++)?{??????????????if?(str1[i?-?1]?==?str2[j?-?1])?{??????????????????veca[i][j]?=?veca[i?-?1][j?-?1]?+?1;??????????????}??????????????else?{??????????????????if?(veca[i?-?1][j]?>=?veca[i][j?-?1])??????????????????????veca[i][j]?=?veca[i?-?1][j];??????????????????else??????????????????????veca[i][j]?=?veca[i][j-1];??????????????}??????????}??????}??????return?veca[str1.length()][str2.length()];??}????int?main()?{??????string?input;??????getline(cin,?input);??????????stringstream?ss(input);??????????string?str1,?str2;??????ss?>>?str1;??????ss?>>?str2;??????//將veca初始化為一個二維數組,其行列值分別為str1和str2的長度加1??????//二維數組veca記錄的是兩個字符串Xi和Yj的LCS長度??????vector<vector<int>?>?veca(str1.length()?+?1,?vector<int>(str2.length()?+?1));??????cout?<<?LCS_length(str1,?str2,?veca)?<<?endl;??????return?0;??}??
結果:
?
動態規劃解決LCS問題的時間復雜度為?O(mn),這比簡單的遞歸實現要快多了。空間復雜度是O(mn),因為使用了一個動態規劃表。
要輸出一個LCS的內容
和上面的程序比,只需要多一個二維數組記錄在遍歷中所選擇的子問題的最優解即可。如下程序:
?
[cpp]?view plain?copy
//輸出最長公共子串(LCS)??//二維數組veca記錄的是兩個字符串Xi和Yj的LCS長度??int?LCS_length(const?string?&str1,?const?string?&str2,???????????????????vector<vector<int>?>?&veca,?vector<vector<int>?>?&vecb)?{??????int?i,?j;??????int?biggest?=?0;??????if?(str1?==?""?||?str2?==?"")??????????return?0;????????for?(i?=?0;?i?<=?str1.length();?i++)?{??????????veca[i][0]?=?0;??????}??????for?(j?=?0;?j?<=?str2.length();?j++)?{??????????veca[0][j]?=?0;??????}??????for?(i?=?1;?i?<=?str1.length();?i++)?{??????????for?(j?=?1;?j?<=?str2.length();?j++)?{??????????????//如果Xi-1?==?Yj-1,那么最長子序列為veca[i?-?1][j?-?1]?+?1??????????????//此時將vecb[i][j]?=?1表明str1[i-1]是子問題LCS的一個元素??????????????if?(str1[i?-?1]?==?str2[j?-?1])?{??????????????????veca[i][j]?=?veca[i?-?1][j?-?1]?+?1;??????????????????vecb[i][j]?=?1;??????????????}??????????????else?{??????????????????if?(veca[i?-?1][j]?>=?veca[i][j?-?1])?{??????????????????????veca[i][j]?=?veca[i?-?1][j];??????????????????????vecb[i][j]?=?2;??????????????????}??????????????????else?{??????????????????????veca[i][j]?=?veca[i][j-1];??????????????????????vecb[i][j]?=?3;??????????????????}??????????????}??????????}??????}??????return?veca[str1.length()][str2.length()];??}????//該函數用于輸出一個LCS的序列??//這里輸出的順序是先向上尋找,再向左尋找??void?PrintOneLCS(vector<vector<int>?>?&vecb,?string?&str1,?int?i,?int?j)?{??????if?(i?==?0?||?j?==?0)??????????return;??????if?(vecb[i][j]?==?1)?{??????????PrintOneLCS(vecb,?str1,?i?-?1,?j?-?1);??????????cout?<<?str1[i?-?1]?<<?"?";??????}??????else?if?(vecb[i][j]?==?2)??????????PrintOneLCS(vecb,?str1,?i?-1,?j);??????else??????????PrintOneLCS(vecb,?str1,?i,?j?-?1);??}????int?main()?{??????string?input;??????getline(cin,?input);??????stringstream?ss(input);??????string?str1,?str2;??????ss?>>?str1;??????ss?>>?str2;??????//將veca初始化為一個二維數組,其行列值分別為str1和str2的長度加1??????//二維數組veca記錄的是兩個字符串Xi和Yj的LCS長度??????//二維數組vecb[i][j]記錄veca[i][j]時所選擇的子問題的最優解??????vector<vector<int>?>?veca(str1.length()?+?1,?vector<int>(str2.length()?+?1));??????vector<vector<int>?>?vecb(str1.length()?+?1,?vector<int>(str2.length()?+?1));??????cout?<<?LCS_length(str1,?str2,?veca,?vecb)?<<?endl;??????PrintOneLCS(vecb,?str1,?str1.length(),?str2.length());??????return?0;??}??
?
求一個LCS內容也可以不借助輔助二維數組vecb而是用下面小節的方法,
[cpp]?view plain?copy
//該函數用于輸出一個LCS的序列,使用下一小節的方法??//這里輸出的順序是先向左尋找,再向上尋找??void?PrintOneLCS(string?&str1,?string?&str2,?int?i,?int?j,???????vector<vector<int>?>?&veca)?{??????????string?lcs_str;??????????while?(i?>?0?&&?j?>?0)?{??????????????if?(str1[i?-?1]?==?str2[j?-?1])?{??????????????????lcs_str?=?str1[i?-?1]?+?lcs_str;??????????????????--i;??????????????????--j;??????????????}??????????????else?{??????????????????//如果左邊存在LCS就從左邊找否則再從右邊找??????????????????if?(veca[i?-?1][j]?>=?veca[i][j?-?1])??????????????????????--i;??????????????????else??????????????????????--j;??????????????}??????????}??????????cout?<<?lcs_str?<<?endl;??}??
如下代碼:
?
?
要輸出所有LCS的內容
兩個字符串對應的最長公共子序列不一定唯一,這個程序輸出所有的LCS內容。
基本思想是:
具體參考文章:http://blog.csdn.net/lisonglisonglisong/article/details/41596309
代碼:
?
[cpp]?view plain?copy
#include?<vector>??#include?<iomanip>??#include?<set>??#include?<string>??#include?<map>??#include?<iostream>??#include?<string.h>??#include?<sstream>??using?namespace?std;????set<string>?all_lcs;?//注意這里要用set去除重復的LCS??//最長公共子串(LCS)??//二維數組veca[i][j]記錄的是兩個字符串Xi和Yj的LCS長度??int?LCS_length(const?string?&str1,?const?string?&str2,?vector<vector<int>?>?&veca)?{??????int?i,?j;??????int?biggest?=?0;??????if?(str1?==?""?||?str2?==?"")??????????return?0;????????for?(i?=?0;?i?<=?str1.length();?i++)?{??????????veca[i][0]?=?0;??????}??????for?(j?=?0;?j?<=?str2.length();?j++)?{??????????veca[0][j]?=?0;??????}??????for?(i?=?1;?i?<=?str1.length();?i++)?{??????????for?(j?=?1;?j?<=?str2.length();?j++)?{??????????????if?(str1[i?-?1]?==?str2[j?-?1])?{??????????????????veca[i][j]?=?veca[i?-?1][j?-?1]?+?1;??????????????}??????????????else?{??????????????????if?(veca[i?-?1][j]?>=?veca[i][j?-?1])??????????????????????veca[i][j]?=?veca[i?-?1][j];??????????????????else??????????????????????veca[i][j]?=?veca[i][j-1];??????????????}??????????}??????}??????return?veca[str1.length()][str2.length()];??}????//該函數找出所有的LCS的序列,并將其存在vector中??void?PrintAllLCS(string?&str1,?string?&str2,?int?i,?int?j,????????????????????vector<vector<int>?>?&veca,?string?lcs_str)?{??//注意這里形參lcs_str不可以為引用,這里需要每次調用lcs_str都重新生成一個對象??????while?(i?>?0?&&?j?>?0)?{??????????if?(str1[i?-?1]?==?str2[j?-?1])?{??????????????lcs_str?=?str1[i?-?1]?+?lcs_str;?//逆向存放??????????????--i;??????????????--j;??????????}??????????else?{??????????????if?(veca[i?-?1][j]?>?veca[i][j?-?1])?//向左走??????????????????--i;??????????????else?if?(veca[i?-?1][j]?<?veca[i][j?-?1])?//向上走??????????????????--j;??????????????else?{?//此時向上向右均為LCS的元素??????????????????PrintAllLCS(str1,?str2,?i?-?1,?j,?veca,?lcs_str);??????????????????PrintAllLCS(str1,?str2,?i,?j?-?1,?veca,?lcs_str);??????????????????return;??????????????}??????????}??????}??????cout?<<?"???"?<<?lcs_str?<<?endl;??????all_lcs.insert(lcs_str);??}??int?main()?{??????string?input;??????getline(cin,?input);??????stringstream?ss(input);??????string?str1,?str2;??????ss?>>?str1;??????ss?>>?str2;??????//將veca初始化為一個二維數組,其行列值分別為str1和str2的長度加1??????//二維數組veca記錄的是兩個字符串Xi和Yj的LCS長度??????vector<vector<int>?>?veca(str1.length()?+?1,?vector<int>(str2.length()?+?1));??????cout?<<?LCS_length(str1,?str2,?veca)?<<?endl;????????string?lcs_str;??????PrintAllLCS(str1,?str2,?str1.length(),?str2.length(),?veca,?lcs_str);??????set<string>::iterator?iter?=?all_lcs.begin();??????while?(iter?!=?all_lcs.end())?{??????????cout?<<?*iter++?<<?endl;??????}??????return?0;??}??
?
如圖所示的兩個字符串共有三個LCS。
二、最長公共子串
描述:
計算兩個字符串的最大公共子串(Longest Common Substring)的長度,字符不區分大小寫。
輸入:
輸入兩個字符串
輸出:
輸出一個整數
樣例輸入:
asdfas werasdfaswer
樣例輸出:
6
?
這里的最大公共字串要求的字串是連續的。
求字串的方法和求子序列方法類似:
當str1[i] == str2[j]時,子序列長度veca[i][j] = veca[i - 1][j - 1] + 1;只是當str1[i] != str2[j]時,veca[i][j]長度要為0,而不是max{veca[i - 1][j], veca[i][j - 1]}。
?
[cpp]?view plain?copy
#include?<vector>????#include?<string>????#include?<iostream>????#include?<string.h>????#include?<sstream>????using?namespace?std;????????int?LCS_length(const?string?&str1,?const?string?&str2,?vector<vector<int>?>?&veca)?{????????int?i,?j;????????int?biggest?=?0;????????if?(str1?==?""?||?str2?==?"")????????????return?0;????????for?(i?=?0;?i?<=?str1.length();?i++)?{????????????veca[i].resize(str2.length()?+?1,?0);????????}????????for?(j?=?0;?j?<=?str2.length();?j++)?{????????????veca[0][j]?=?0;????????}????????for?(i?=?1;?i?<=?str1.length();?i++)?{????????????for?(j?=?1;?j?<=?str2.length();?j++)?{????????????????if?(str1[i?-?1]?==?str2[j?-?1])?{????????????????????veca[i][j]?=?veca[i?-?1][j?-?1]?+?1;????????????????????if?(veca[i][j]?>?biggest)????????????????????????biggest?=?veca[i][j];????????????????}????????????????else???????//可以看出,求最長子串和求最長子序列差別僅僅在這里??????????????????veca[i][j]?=?0;????????????????????????????}????????}????????return?biggest;????}????????int?main()?{????????string?input;????????getline(cin,?input);????????stringstream?ss(input);????????string?str1;????????ss?>>?str1;????????string?str2;????????ss?>>?str2;????????vector<vector<int>?>?veca(str1.length()?+?1);????????cout?<<?LCS_length(str1,?str2,?veca)?<<?endl;????????return?0;????}????
同樣適用求最長子序列的測試數據,得到它們的公共最長子串長度為2,而它們的公共最長子序列長度為4.
?
?
三、動態規劃的其它題目
1、硬幣面值組合問題
http://www.cnblogs.com/python27/archive/2013/09/05/3303721.html
?
2、最長遞增子序列
? ? ? ? ? ?除了動態規劃,該題還有其他解法。
?
3、數組最大子數組和的最大值
http://www.ahathinking.com/archives/120.html
?
3、動態規劃之鋼條分割
?
4、計算兩個字符串的相似度(編程之美)
? ? ? ?該文章原理說得比較清楚:點擊打開鏈接
? ? ? ?這里是代碼:點擊打開鏈接
?
5、求每一對頂點之間的最短路徑:Floyd-Warshall算法
?
轉自:https://blog.csdn.net/u013074465/article/details/45392687
總結
以上是生活随笔為你收集整理的动态规划算法---求最长公共子序列的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。