首页 > 代码库 > dp之最长公共子序列

dp之最长公共子序列

例1:给你两个字符串,找出最长子序列的长度。

对于字符串t, 字符串s,给定特定的i, j代表t,s的位置,只存在三种情况:

  1. i == 0 ||  j==0, M[i][j] = 0;

  2. t[i] == s[j], M[i][j] = min(M[j-1][j-1] + 1, M[i-1][j], M[i][j-1]);//.....不知道怎么解释。。。。

  3. t[i] != s[j] , M[i][j] = min(M[i-1][j], M[i][j-1]);//。。。。。

经典dp题

代码:

#include <iostream>
#include <cstring>
using namespace std;
string s, t;
int M[1000][1000];
int hasgo[1000][1000];
int dp(int a, int b)
{
    if(hasgo[a][b] > 0) return M[a][b];
    if(!a || !b) return 0;
    if(s[a-1] == t[b-1])
    {
        M[a][b] = max(dp(a-1, b-1) + 1, M[a][b]);
    }
    M[a][b] = max(M[a][b], dp(a, b - 1));
    M[a][b] = max(M[a][b], dp(a - 1, b));
    hasgo[a][b] = 1;
    return M[a][b];
}
int main()
{
    while(cin >> s >> t)
    {
        memset(M, 0, sizeof(M));
        memset(hasgo, 0, sizeof(hasgo));
        cout << dp(s.size(), t.size()) << endl;
    }
    return 0;
}

升级版最长公共子序列

例2:给你n条字符串,找出最长公共子序列的长度。

如果按照上面的思路,如果是三条,弄个三维数组就好了,几条字符串就弄几维数组,但这题维数没法确定,,,就涉及到dp中一个常用的技巧,,子问题编码,对于A(a[1], a[2], a[3] .... a[n])代表

各个串中的位置,给这个数组独一无二的编码: index = a[1] + a[2] * len[1] + a[3] * len[2] + ... + a[n] * len[n-1],   这样一个一维数组M[index]就可以表示每种状态的最长子串了。

(len 是各个串的长度)
1. 有一个为0, M[index] = 0;

2. 都相等的时候, M[index] = min(M[index], M[index‘] + 1), index‘ = a[i‘] = a[i] - 1;for(int i = 1; i <= n; i++),A[i]--; M[index] = M[index‘];

3.for(int i = 1; i <= n; i++),A[i]--; M[index] = M[index‘];(当然,是在len1 * len2 * len 3 *...在可接受的范围内。。。)

代码

 

#include <iostream>
#include <cstring>
using namespace std;
///多个字符串找最大公共子串,对于每一个串确定的位置(d1, d2, d3,...,dn),可能存在三种情况,当前位置的字母都是相同的,
int *len, *A, *M, *xishu;///字符串长度, 系数, 最长公共子序列,系数单位
char str[105][105];
int n;
int hasgo[30005];
int solve(int index)
{
    if(hasgo[index] > 0) return M[index];
    for(int i = 0; i < n; i++)
        if(!A[i]){hasgo[index] = 1; return 0;}
    int ok = 1;
    for(int i = 0; i < n - 1; i++)
        if(str[i][A[i]] != str[i+1][A[i+1]]) ok = 0;
    if(ok)
    {
        int ii = index;
        for(int i = 0; i < n; i++)
         {
             A[i]--;
             ii -= xishu[i];
         }
        M[index] = max(solve(ii) + 1, M[index]);
        for(int i = 0; i < n; i++)
            A[i]++;
    }
    int ii = index;
    for(int i = 0; i < n; i++)
    {
        ii -= xishu[i];
        A[i]--;
        M[index] = max(solve(ii), M[index]);
        A[i]++;
        ii += xishu[i];
    }
    hasgo[index] = 1;
    return M[index];
}
int main()
{
    int t;
    cin >> t;
    while(t--)
    {
        cin >> n;
        len = new int[n];
        A = new int[n];
        M = new int[30005]();
        xishu = new int[n];
        int ii = 0;
       // memset(M, 0, sizeof(M)*30005);
        memset(hasgo, 0, sizeof(hasgo));
        for(int i = 0; i < n; i++)
        {
            cin >> str[i];
        }
        for(int i = 0; i < n; i++)
        {
            len[i] = strlen(str[i]);
            A[i] = len[i];
            xishu[i] = (!i ? 1 : xishu[i-1]*len[i-1]);
            ii += A[i] * xishu[i];
        }
        cout << solve(ii) << endl;
        delete []M;
        delete []A;
        delete []xishu;
        delete []len;
    }
}

 

dp之最长公共子序列