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;
    }
}
时间: 2024-08-06 19:13:25

dp之最长公共子序列的相关文章

UVA 10100- Longest Match(dp之最长公共子序列)

题目地址:UVA 10100 题意:求两组字符串中最大的按顺序出现的相同单词数目. 思路:将字串中的连续的字母认作一个单词,依次计算出两个字符串中的单词,其中第1个字符串的单词序列为t1.word[1]-..t1.word[n],第2个字符串的单词序列为t2.word[1]-..t2.word[m].然后将每个单词当成一个字符,使用LCS算法计算出两个字符串的最长公共子序列,该序列的长度就是最长匹配. #include <stdio.h> #include <math.h> #in

【DP】最长公共子序列

Description 字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列.令给定的字符序列X="x0,x1,-,xm-1",序列Y="y0,y1,-,yk-1"是X的子序列,存在X的一个严格递增下标序列<i0,i1,-,ik-1>,使得对所有的j=0,1,-,k-1,有xij = yj.例如,X="ABCBDAB",Y="BCDB"是X的一个子序列. 对给

(hdu step 3.2.2)Common Subsequence(简单dp:求最长公共子序列的长度)

在写题解之前给自己打一下广告哈~..抱歉了,希望大家多多支持我在CSDN的视频课程,地址如下: http://edu.csdn.net/course/detail/209 题目: Common Subsequence Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 976 Accepted Submission(s): 538   Probl

hdu1159-Common Subsequence(DP:最长公共子序列LCS)

Common Subsequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 49216    Accepted Submission(s): 22664 Problem Description A subsequence of a given sequence is the given sequence with some el

[dp]LCS最长公共子序列

https://www.51nod.com/tutorial/course.html#!courseId=4 复杂度:${\rm O}(nm)$ 转移方程: 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 int n,m; 5 int dp[1002][1002]; 6 char path[1002]; 7 string s,t; 8 int main(){ 9 cin>>s>

Codeforcs 1114D Flood Fill (区间DP or 最长公共子序列)

题意:给你n个颜色块,颜色相同并且相邻的颜色块是互相连通的(连通块).你可以改变其中的某个颜色块的颜色,不过每次改变会把它所在的连通块的颜色也改变,问最少需要多少次操作,使得n个颜色块的颜色相同. 例如:[1, 2, 2, 3, 2]需要2步:[1, 2, 2, 3, 2] -> [1, 2, 2, 2, 2] -> [2, 2, 2, 2, 2]. 思路:我们先把颜色块压缩(即把本来颜色相同并且相邻的颜色块合并).容易发现,如果出现了形如[1, 2, 3, 1]这种情况, 那么显然先合并两个

一天一道算法题(4)---最长公共子序列

题目 给定两个字符串str1和str2,返回两个字符串的最长公共子序列 解析 本题是非常经典的动态规划问题,先来介绍求解动态规划表的过程.如果str1的长度为M,str2的长度为N,生成大小为M*N的矩阵dp,行数为M,列数为N.dp[i][j]的含义是str1[0..i]和str2[0..j]的最长公共子序列的长度.从左到右,再从上到下计算矩阵dp. 1.矩阵dp第一列即dp[0..M-1][0],dp[i][0]的含义是str1[0..i]与str2[0]的最长公共子序列的长度. 2.矩阵d

UVA 111 History Grading (最长公共子序列)

History Grading Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Background Many problems in Computer Science involve maximizing some measure according to constraints. Consider a history exam in which students are asked to put s

LIS(最长递增子序列)和LCS(最长公共子序列)的总结

最长公共子序列(LCS):O(n^2) 两个for循环让两个字符串按位的匹配:i in range(1, len1) j in range(1, len2) s1[i - 1] == s2[j - 1], dp[i][j] = dp[i - 1][j -1] + 1; s1[i - 1] != s2[j - 1], dp[i][j] = max (dp[i - 1][j], dp[i][j - 1]); 初始化:dp[i][0] = dp[0][j] = 0; 伪代码: dp[maxn1][ma