目錄
1 子序列概念
一個給定序列的子序列是在序列中刪除若干個元素後得到的序列。在這裏,首先說明子序列的概念(切記子序列非子集的概念),例如是序列的一個子序列,則序列在序列中相對應的下標爲,序列和的下標都是從1開始。
2 問題描述
如果給定兩個序列和序列,當另外一個序列即是的子序列又是的子序列,那麼稱序列是序列和序列的公共子序列。
假如,這兩個序列,則序列是和的一個公共子序列,但他不是一個最長的公共子序列。而序列纔是一個最長的公共子序列,他的長度爲4,應爲序列和沒有長度大於四的公共子序列。
瞭解了最長公共子序列,那麼最長公共子序列問題就是在給定的兩個序列和中,找出他們最長的公共子序列這樣一個問題。
2.1 問題分析:
設序列和序列的最長公共子序列是。
(1)如果,則,並且是和的最長公共子序列。
(2)如果並且,那麼是和的一個最長公共子序列。
(3)如果並且,那麼是和的一個最長公共子序列。
在這裏,其中;;.
2.2 動態規劃求解公式
根據以上問題分析,我們要找出序列和的最長公共子序列,可以按照以下遞歸進行求解:當時,(即兩個序列最後一個字符相同),那我們的變爲求解和得最長公共子序列在加上即可,就可以得到和的一個最長公共子序列。當時,必須求解兩個問題,即找出和,和兩者當中較長的一個公共子序列 ,即得到最終結果,所以我們建立了以上的遞歸公式求解。我們用記錄序列和的最長公共序列的長度,其中當i=0或者j=0時表示此時最長公共序列爲。記錄的值是由哪一個子問題求解得到的。和的最長公共子序列記錄在。
下面我們就可以寫出求最長公共子序列的遞推公式:
2.3 算法展示
#include "pch.h"
#include <iostream>
using namespace std;
int LengthString(char *x, char *y, int m, int n, int **c, int **b)
{
//x和y是兩個字符串,m和n分別是其長度,二維數組保存最長公共序列長度,b數組記錄在哪個子問題下得到的解
for (int i = 0;i <= m;i++)
c[i][0] = 0;
for (int j = 0;j <= n;j++)
c[0][j] = 0;
for (int i = 1;i <= m;i++)
{
for (int j = 1;j <= n;j++)
{
if (x[i] == y[j])
{
c[i][j] = c[i - 1][j - 1] + 1;
b[i][j] = 1;
}
else
if (c[i][j - 1] > c[i - 1][j])
{
c[i][j] = c[i][j - 1];
b[i][j] = 2;
}
else
{
c[i][j] = c[i - 1][j];
b[i][j] = 3;
}
}
}
return c[m][n];
}
void LCS(int i, int j, char *x, int **b)
{
if (i == 0 || j == 0)
return;
if (b[i][j] == 1)
{
LCS(i - 1, j - 1, x, b);
cout << x[i-1] << " ";
}
else
if (b[i][j] == 2)LCS(i - 1, j, x, b);
else
LCS(i, j - 1, x, b);
}
int main()
{
int charNum_1;
int charNum_2;
cout << "請輸入兩個字符串的長度:" << endl;
cin >> charNum_1 >> charNum_2;
char *x = new char[charNum_1];
char *y=new char[charNum_2];
int **b = new int*[charNum_1];
int **c = new int*[charNum_1];
for (int i = 0;i <= 7;i++)//申請空間
{
b[i] = new int[charNum_2];
c[i] = new int[charNum_2];
}
for (int i = 0;i <= charNum_1;i++)//初始化
{
for (int j = 0;j <= charNum_2;j++)
{
b[i][j] = 0;
c[i][j] = 0;
}
cout << endl;
}
cout << "請輸入字符串 1:";
for (int i = 0;i <charNum_1;i++)
{
cin >> x[i];
}
cout << "請輸入字符串 2:";
for (int i = 0;i <charNum_2;i++)
{
cin >> y[i];
}
int len=LengthString(x, y, charNum_1, charNum_2, c, b);
cout << "最長公共子串長度是:" << len << endl;
cout << "最長公共子串是:";
LCS(charNum_1, charNum_2, x, b);
for (int i = 0; i <= charNum_1; i++) //釋放動態申請的二維數組空間
delete[] c[i];
delete[] c;
}
由於每個數組單元計算耗費的時間,所以算法LengthString的時間複雜度爲
2.4 求解最長序列輸出
由以上我們求出的只是最長公共子序列的長度,最長子序列是什麼還沒求出。所以我們根據數組繼續構造最長子序列,首先從開始,在數組中依次搜索,當時,表示和的最長公共子序列是由和加上得到的,當表示和的最長公共子序列和與的最長公共子序列相同,當時,表示和的最長公共子序列和與的子序列相同,由此我們得到遞歸求解公共子序列的算法。
void LCS(int i, int j, char *x, int **b)
{
//i和j分別表示序列x和y的長度
if (i == 0 || j == 0)
return;
if (b[i][j] == 1)
{
LCS(i - 1, j - 1, x, b);
cout << x[i-1] << " ";
}
else
if (b[i][j] == 2)LCS(i - 1, j, x, b);
else
LCS(i, j - 1, x, b);
}
在算法中,由於每次遞歸使得i和j的值每次都減小1,所以算法的時間複雜度爲.