C++動態規劃實現查找最長公共子序列

問題描述:ios

給定兩個序列X={x1,x2,…,xm}和Y={y1,y2,…,yn},找出X和Y的最長公共子序列。(給定兩個序列X和Y,當另外一序列Z既是X的子序列又是Y的子序列時,稱Z是序列X和Y的公共子序列。)算法

細節須知(與以前隨筆的對比):windows

將由數組存儲起來一併輸出至文件修改成邊運行邊輸出,增長了程序的魯棒性。數組

算法原理:app

a.最長公共子序列的結構dom

對X的全部子序列,檢查它是否也是Y的子序列,從而肯定它是否爲X和Y的公共子序列。而且在檢查過程當中記錄最長的公共子序列。X的全部子序列都檢查事後便可求出X和Y的最長公共子序列。X的每一個子序列相應於下標集{1,2,…,m}的一個子集。spa

b.子問題的遞歸結構設計

要找出X和Y的最長公共子序列,可按如下方式遞歸計算:當xm=yn時,找出Xm-1和Yn-1的最長公共子序列,而後在其尾部加上xm(=yn)便可獲得X和Y的最長公共子序列。當xm≠yn時,必須解兩個子問題,即找出Xm-1和Y的一個最長公共子序列及X和Yn-1的一個最長公共子序列。這兩個公共子序列中較長者即爲X和Y的最長公共子序列。code

c.計算最優值orm

利用動態規劃算法自底向上地計算最優值。

d.構造最長公共子序列

首先從b[m][n]開始,依其值在數組b中搜索。當b[i][j]=1時,表示Xi和Yj的最長公共子序列

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<stack>
  4 #include<ctime>
  5 #include<iostream>
  6 #include<fstream>
  7 #include<algorithm>
  8 #include<windows.h>
  9 using namespace std;
 10 LARGE_INTEGER nFreq;//LARGE_INTEGER在64位系統中是LONGLONG,在32位系統中是高低兩個32位的LONG,在windows.h中經過預編譯宏做定義
 11 LARGE_INTEGER nBeginTime;//記錄開始時的計數器的值
 12 LARGE_INTEGER nEndTime;//記錄中止時的計數器的值
 13 #define N 10000
 14 //const int SIZE_CHAR = 10000; //生成32 + 1位C Style字符串
 15 const char CCH[] = "_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
 16 int dp[N][N];
 17 char c;
 18 int main(void)
 19 {
 20     //char a[N];
 21     //char b[N];
 22     //char a[SIZE_CHAR+2];
 23     //char b[SIZE_CHAR+2];
 24     ofstream fout;
 25     int m = 0,i = 0;
 26     int SIZE_CHAR;
 27     cout<<"Please enter the number of times you want to run the program:";        //輸入程序運行次數
 28     cin>>m;
 29     //int SIZE[m];
 30     double cost;
 31     //double runtime[m];
 32     srand((unsigned)time(NULL));
 33     fout.open("data.txt",ios::app);
 34     if(!fout){
 35         cerr<<"Can not open file 'data.txt' "<<endl;
 36         return -1;
 37     }
 38     fout.setf(ios_base::fixed,ios_base::floatfield);   //防止輸出的數字使用科學計數法
 39     for(i = 0; i < m; i++){
 40         //SIZE_CHAR=10000+RAND_MAX*(rand()%300)+rand();           //RAND_MAX=32767,隨機生成數據量
 41         SIZE_CHAR = rand() % 10000;
 42         fout<<SIZE_CHAR<<",";
 43         // SIZE[i]=SIZE_CHAR;                                      //限定數據規模爲10000~9872867
 44         char a[SIZE_CHAR + 1] = {'\0'};
 45         char b[SIZE_CHAR + 1] = {'\0'};
 46         cout<<"☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆The "<<i+1<<"th test's string size is:"<<SIZE_CHAR<<"☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆"<<endl;
 47         for (int i = 0; i < SIZE_CHAR; ++i){
 48             int x = rand() / (RAND_MAX / (sizeof(CCH) - 1));
 49             a[i] = CCH[x];
 50         }
 51         cout<<"The first random sting is:" <<a <<endl;
 52         for (int i = 0; i < SIZE_CHAR; ++i){
 53             int x = rand() / (RAND_MAX / (sizeof(CCH) - 1));
 54             b[i] = CCH[x];
 55         }
 56         cout<<"The second random string is:" <<b <<endl;
 57         cout<<"The longest common subsequence is:";
 58         QueryPerformanceFrequency(&nFreq);//獲取系統時鐘頻率
 59         QueryPerformanceCounter(&nBeginTime);//獲取開始時刻計數值
 60         int la=strlen(a);
 61         int lb=strlen(b);
 62         memset(dp,0,sizeof(dp));
 63         for(int i=1; i<=la; i++){
 64             for(int j=1; j<=lb; j++){
 65                 if(a[i-1]==b[j-1])
 66                     dp[i][j]=dp[i-1][j-1]+1;
 67                 else
 68                     dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
 69             }
 70         }
 71         int i=la,j=lb;
 72         stack<char>s;
 73         while(dp[i][j]){
 74             if(dp[i][j]==dp[i-1][j]){//來自於左方向
 75                 i--;
 76             }
 77             else if(dp[i][j]==dp[i][j-1]){//來自於上方向
 78                 j--;
 79             }
 80             else if(dp[i][j]>dp[i-1][j-1]){//來自於左上方向
 81                 i--;
 82                 j--;
 83                 s.push(a[i]);         //壓棧以便倒序輸出
 84         }
 85     }
 86     while(!s.empty())
 87     {
 88         c=s.top();
 89         printf("%c",c);
 90         s.pop();
 91     }
 92     cout<<endl;
 93     QueryPerformanceCounter(&nEndTime);//獲取中止時刻計數值
 94     cost=(double)(nEndTime.QuadPart - nBeginTime.QuadPart) / (double)nFreq.QuadPart;
 95     fout<<cost<<endl;
 96     //runtime[i]=cost;
 97     cout<<"The running time is:"<<cost<<" s"<<endl;
 98     }
 99     fout.close();
100     cout<<"Success!"<<endl;
101     return 0;
102 }

程序設計思路:

設序列X={x1,x2,…,xm}和Y={y1,y2,…,yn}的最長公共子序列爲Z={z1,z2,…,zk},則

a.若xm=yn,則zk=xm=yn,且Zk-1是Xm-1和Yn-1的最長公共子序列。

b.若xm≠yn,且zk≠xm,則Z是Xm-1和Y的最長公共子序列。

c.若xm≠yn,且zk≠yn,則Z是X和Yn-1的最長公共子序列。

其中,Xm-1={x1,x2,…,xm-1};Ym-1={y1,y2,…,yn-1};Zk-1={z1,z2,…,zk-1}。

② 子問題的遞歸結構

首先創建子問題最優值的遞歸關係。用c[i][j]記錄序列Xi和Yj的最長公共子序列的長度。其中,X={x1,x2,…,xm};Y={y1,y2,…,yn}。當i=0或j=0時,空序列是Xi和Yj的最長公共子序列,故此時c[i][j]=0。在其餘狀況下,由最優子結構性質課件裏遞歸關係以下:

③計算最優值

以序列X和Y做爲輸入。輸出兩個數組c和b。其中c[i][j]存儲Xi和Yj的最長公共子序列的長度,b[i][j]記錄c[i][j]的值是由哪個子問題的解獲得的,這在構造最長公共子序列時要用到。問題的最優值,即X和Y的最長公共子序列的長度記錄與c[m][n]中。

④構造最長公共子序列

首先從b[m][n]開始,依其值在數組b中搜索。當b[i][j]=1時,表示Xi和Yj的最長公共子序列是由Xi-1和Yj-1的最長公共子序列在尾部加上xi所獲得的子序列;當b[i][j]=2時,表示Xi和Yj的最長公共子序列與Xi-1和Yj的最長公共子序列相同;當b[i][j]=3時,表示Xi和Yj的最長公共子序列與Xi和Yj-1的最長公共子序列相同。

時間複雜性分析:

a.計算最優值

因爲每一個數組單元的計算耗費O(1)的時間,此部分算法耗時爲O(mn)。

b.構造最長公共子序列

該算法每一次遞歸調用使i或j減1,所以算法的計算時間爲O(m+n)。

生成的數據可導入EXCEL中進行數據分析生成分析圖表。

相關文章
相關標籤/搜索