关于求解最长公共子序列的两种思路

一、题目描述

求取两个给定序列的最长子序列,仅需要求取出其中一个最长子序列即可。

比如序列 X 为 ABCBDAB ,序列 Y 为 BDCABA,则其公共最长子序列之一为 BCBA(可能不唯一)。

关于求解所有最长公共子序列的思路可以移步我的另一篇博文:

https://blog.csdn.net/qq_40697071/article/details/89059844

二、解题思路

这道题的解法一般来说有两种思路,第一种就是普通的暴力穷举法,即对每一种情况都进行列举,然后将两个序列的子序列从头到尾的全部进行比较,因为对于一个序列若其所含元素的数量为 m 时,其所具有的子集的数量为 2^m,然后对于其中每一个子集与另一个序列进行比较时的时间复杂度为 O(n),因此可以计算出穷举法的时间复杂度为 O(n^(2^m)),这种时间复杂度可以说是很可怕了,所以我们不采取这种解法。

另一种解法是通过使用是用动态规划的思路来进行求解,求解的过程类似于 0、1背包问题,其主要的思路是从后向前对两个序列进行判断,因为当我们从后向前进行判断的时候,只存在两种情况,即两个序列的最后元素相等,或者不相等,假如两个序列的最后一个元素相等,那么就是说最后这个元素肯定是其最长公共子序列的最后一个元素,所以将其记录,然后两个序列同时抛弃最后一个元素。假如两个序列的最后元素不相等,那么就再分两种情况,一种是第一个序列抛弃最后一个元素然后在与第二个序列进行比较,另一种是第二个序列抛弃最后一个元素然后再与第一个元素进行比较,之后的过程其实就是一个重复之前操作的过程。

通过上面的思路,其实我们已经可以大致确定这道题的一个解题的思路,而且我们可以发现这个解法其实就是一种动态规划的思想,即对之前已经计算出来的结果进行保存,后面的计算可以直接依赖于之前的计算结果,这样的话我们可以将两个序列抽象为一个二位数组,行为一个序列,列为一个序列,每个点对应的是从开头到当前为止最长的公共子序列长度,这样的话对于每一个点,假如当前其所对应的在两个序列中的元素相等,即为上面说的第一种情况,那么我们就可以直接使用其西北角的已经计算出来的值加一,相当于直接在之前最长公共子序列的长度加一就可以,反之,假如当前的位置在两个序列中对应的元素不相等,那么就直接取其上方值或者左方值中的最大值即可,因为其上方值和左方值分别对应着上面的第二种情况中的两种情况,即当两个序列的最后一个元素不相等的时候,中间一个序列不动,另一个抛弃尾元素,当两个都这样操作后,取其最大值即可。

之后就是对于这种思路的两种实现方式,一种是使用两个数组,即一个数组保存上述的数据,另一个数组则用来进行标志保存,第二数组的目的是为了最后去求取最长的公共子序列,注意,对于上面的解法也就是单个数组的时候,数组中保存的仅仅是当前位置所对应的最长公共子序列,我们还需要通过另一个方法来确定最后的子序列。用标志数组进行保存的目的是记录当前最长子序列的对于上一步的选择,是直接加一还是抛弃一个尾元素,然后再通过从数组的右下角向左上角递归求解,来求得最后的最长公共子序列。

在上面一种解法中,我们需要创建两个数组,但其实通过思考,我们发现其实只通过一个数组就可以完全满足我们的需要,即当当前位置的上方和当前位置的最长公共子序列长度相等时,我们就默认选择向上走,而不相等的时候就可以认为当前元素为两个序列的一个公共元素,所以直接添加即可。需要注意的是,对于第一个数组初始化,我们应当默认多创建一行和一列,来便于对两个序列的首元素进行判断。

三、代码实现

// 思路一:利用标志数组求解
#include <iostream>

// 根据标志数组求解最长公共子序列
void printLcs(vector<char> charArray, vector<vector<int>> recordRoadArray, int row, int col){
    
    if(row == 0 || col == 0)
        return;
    
    if(recordRoadArray[row][col] == 1){
        printLcs(charArray, recordRoadArray, row-1, col-1);
        cout << charArray[row-1];
    }else if(recordRoadArray[row][col] == 2)
        printLcs(charArray, recordRoadArray, row-1, col);
    else
        printLcs(charArray, recordRoadArray, row, col-1);
}

// 求解标志数组和整理动态规划数组
void lcsLength(vector<char> firstCharArray, vector<char> secondCharArray, int rowLength, int colLength){
    
    vector<vector<int>> recordArray = vector<vector<int>>(rowLength, vector<int>(colLength, 0));
    vector<vector<int>> recordRoadArray = vector<vector<int>>(rowLength, vector<int>(colLength, 0));
    
    for(int i = 1; i < rowLength; ++i)
        for(int j = 1; j < colLength; ++j){
            if(firstCharArray[i-1] == secondCharArray[j-1]){
                recordArray[i][j] = recordArray[i-1][j-1] + 1;
                recordRoadArray[i][j] = 1;
            }else if(recordArray[i-1][j] >= recordArray[i][j-1]){
                recordArray[i][j] = recordArray[i-1][j];
                recordRoadArray[i][j] = 2;
            }else{
                recordArray[i][j] = recordArray[i][j-1];
                recordRoadArray[i][j] = 3;
            }
        }
    
    printLcs(firstCharArray, recordRoadArray, rowLength-1, colLength-1);        
}

int main() {
    
    vector<char> firstCharArray = {'A', 'B', 'C', 'B', 'D', 'A', 'B'};
    vector<char> secondCharArray = {'B', 'D', 'C', 'A', 'B', 'A'};
    
    lcsLength(firstCharArray, secondCharArray, firstCharArray.size()+1, secondCharArray.size()+1);    
}
// 思路二:只使用一个数组来进行求解
#include <iostream>

void printLcs(vector<char> charArray, vector<vector<int>> recordArray, int row, int col){
    
    if(row == 0 || col == 0)
        return;
    
    if(recordArray[row][col] != recordArray[row-1][col]){
        printLcs(charArray, recordArray, row-1, col-1);
        cout << charArray[row-1];
    }else
        printLcs(charArray, recordArray, row-1, col);
}

void lcsLength(vector<char> firstCharArray, vector<char> secondCharArray, int rowLength, int colLength){
    
    vector<vector<int>> recordArray = vector<vector<int>>(rowLength, vector<int>(colLength, 0));
    
    for(int i = 1; i < rowLength; ++i)
        for(int j = 1; j < colLength; ++j){
            if(firstCharArray[i-1] == secondCharArray[j-1])
                recordArray[i][j] = recordArray[i-1][j-1] + 1;
            else if(recordArray[i-1][j] >= recordArray[i][j-1])
                recordArray[i][j] = recordArray[i-1][j];
            else
                recordArray[i][j] = recordArray[i][j-1];
            
        }
    
    printLcs(firstCharArray, recordArray, rowLength-1, colLength-1);        
}

int main() {
    
    vector<char> firstCharArray = {'A', 'B', 'C', 'B', 'D', 'A', 'B'};
    vector<char> secondCharArray = {'B', 'D', 'C', 'A', 'B', 'A'};
    
    lcsLength(firstCharArray, secondCharArray, firstCharArray.size()+1, secondCharArray.size()+1);    
}
发布了277 篇原创文章 · 获赞 33 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_40697071/article/details/88854497