核心是要明白动态规划数组如何求得,以及后续如何根据数组末尾元素反推得到公共序列路径。

现在我们脑海中构建一个二维数组,这个数组纵轴的坐标代表了s1对应的字符下标,横轴代表s2对应的字符下标。

当然,因为动态规划的初值需要给出,我们将数组第0列和第0行的元素全都设为0,表示当字符串为空时,匹配数目为0。

知道初值以后,我们就可以一步步递推求数组了。

1.求动态规划数组

①在循环遍历时,倘若遇到两个字符串中的字符匹配s1[i]=s2[j],那么记录当前匹配字符数的arr[i+1][j+1]就是其斜上角的arr[i][j]+1。(不要忘了由于我们为了动态规划的初值存储,特地为其腾出了第0行和第0列的空间噢)

*为什么不能从左/上角得到其最大值+1呢?

填写动态规划数组的过程其实就是算从0到当前i,j下标位置s1和s2的匹配字符数,当你填写s1第二个a时,你想要用上方的2再+1填进去?不行噢,s1第一个a告诉你它已经和s2的a匹配了,这个时候你就不能再和其匹配了。否则你岂不是计算出了a和aa的公共子序列是aa了嘛。

②字符不相等s1[i]!=s2[j]时,匹配字符数无变化,那么我们就把先前匹配的最大字符数继承过来,可以从左边或上面取最大值。即arr[i + 1][j + 1] = max(arr[i + 1][j], arr[i][j + 1])

2.回溯路径得到公共序列

由于动态规划求数组有两种解决分支,那么这个回溯有两种思路。

way1 (对应题解1):可以根据当前arr[i + 1][j + 1] 是否等于 max(arr[i + 1][j], arr[i][j + 1])判断。

如果相等,找出它左/上的最大值,对应下标--,遍历到这个最大值所在的坐标;

如果不等,那么就必然是从斜上角找了,两个下标都--,遍历到斜上角的坐标。

way2 (对应题解2):根据当前坐标i,j,判断s1[i]是否等于s2[j]。

相等,说明从斜上角找;

不等,从它左/上找。

当进入标红的分支时,说明字符匹配,那么就把这个字符推入栈中。

最后输出栈的内容就好啦。(因为是倒序回溯所以要使用栈)

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * longest common subsequence
     * @param s1 string字符串 the string
     * @param s2 string字符串 the string
     * @return string字符串
     */
    string LCS(string s1, string s2) {
        // write code here
        int m = s1.length()+1;
        int n = s2.length()+1;
        int** arr = new int* [m];
        for (int i = 0; i < m; i++) {
            arr[i] = new int[n];
        }
        for(int i = 0; i < m; i++) {
            arr[i][0] = 0;
        }
        for (int j = 0; j < n; j++) {
            arr[0][j] = 0;
        }
        for (int i = 0; i < m-1; i++) {
            for (int j = 0; j < n-1; j++) {
                if (s1[i] == s2[j]) {
                    arr[i + 1][j + 1] = arr[i][j] + 1;//相等找斜对角
                }
                else {
                    arr[i + 1][j + 1] = max(arr[i + 1][j], arr[i][j + 1]);//不相等从左/上max继承
                }
            }
        }
	  //test output
        cout << "s p ";
        for (int j = 0; j < n; j++) {
            cout << s2[j] << " ";
        }
        cout << "\np ";
        for (int j = 0; j < n; j++) {
            cout << arr[0][j] << " ";
        }
        cout << endl;
        for (int i = 1; i < m; i++) {
            cout << s1[i-1] << " ";
            for (int j = 0; j < n; j++) {
                cout << arr[i][j] << " ";
            }
            cout << endl;
        }//test
        
        stack <char>st;
        int i = m - 1, j = n - 1;
        while (i > 0&&j > 0) {
            //s1[i] != s2[j]
            if (arr[i][j] == max(arr[i - 1][j], arr[i][j - 1])) {
                if (arr[i - 1][j] > arr[i][j - 1]) {
                    i--;
                }
                else {
                    j--;
                }
            }
            //s1[i] == s2[j]
            else {
                st.push(s1[i-1]);
                //cout << s1[i-1] << "test" << endl;
                i--;
                j--;
            }
        }
        string res = "";
        //output
        while (!st.empty()) {
            char ch=st.top();
            res += ch;
            st.pop();
        }
        if (arr[m - 1][n - 1] == 0)
            return "-1";
        return res;
        for (i = 0; i < m; i++) {
            delete[]arr[i];
        }
        delete[]arr;
    }
};

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * longest common subsequence
     * @param s1 string字符串 the string
     * @param s2 string字符串 the string
     * @return string字符串
     */
    string LCS(string s1, string s2) {
        // write code here
        int m = s1.length()+1;
        int n = s2.length()+1;
        int** arr = new int* [m];
        for (int i = 0; i < m; i++) {
            arr[i] = new int[n];
        }
        for(int i = 0; i < m; i++) {
            arr[i][0] = 0;
        }
        for (int j = 0; j < n; j++) {
            arr[0][j] = 0;
        }
        for (int i = 0; i < m-1; i++) {
            for (int j = 0; j < n-1; j++) {
                if (s1[i] == s2[j]) {
                    arr[i + 1][j + 1] = arr[i][j] + 1;//相等找斜对角
                }
                else {
                    arr[i + 1][j + 1] = max(arr[i + 1][j], arr[i][j + 1]);//不相等从左/上max继承
                }
            }
        }
        cout << "s p ";
        for (int j = 0; j < n; j++) {
            cout << s2[j] << " ";
        }
        cout << "\np ";
        for (int j = 0; j < n; j++) {
            cout << arr[0][j] << " ";
        }
        cout << endl;
        for (int i = 1; i < m; i++) {
            cout << s1[i-1] << " ";
            for (int j = 0; j < n; j++) {
                cout << arr[i][j] << " ";
            }
            cout << endl;
        }//test
        
        stack <char>st;
        int i = m - 1, j = n - 1;
        while (i > 0&&j > 0) {
            //s1[i] == s2[j]
            if(s1[i-1] == s2[j-1]) {
                st.push(s1[i-1]);
                //cout << s1[i-1] << "test" << endl;
                i--;
                j--;
            }
            //s1[i] != s2[j]
            else{
                if (arr[i - 1][j] > arr[i][j - 1]) {
                    i--;
                }
                else {
                    j--;
                }
            }
        }
        string res = "";
        //output
        while (!st.empty()) {
            char ch=st.top();
            res += ch;
            st.pop();
        }
        if (arr[m - 1][n - 1] == 0)
            return "-1";
        return res;
        for (i = 0; i < m; i++) {
            delete[]arr[i];
        }
        delete[]arr;
    }
};