算法——LCS最长公共子序列和LIS最长上升子序列——算法详解及例题剖析

一.LCS最长公共子序列

longest comment subsequence
算法详解
模板题
longest comment subsequence
A subsequence of a given sequence is the given sequence with some elements (possible none) left out. Given a sequence X = <x1, x2, …, xm> another sequence Z = <z1, z2, …, zk> is a subsequence of X if there exists a strictly increasing sequence <i1, i2, …, ik> of indices of X such that for all j = 1,2,…,k, xij = zj. For example, Z = <a, b, f, c> is a subsequence of X = <a, b, c, f, b, c> with index sequence <1, 2, 4, 6>. Given two sequences X and Y the problem is to find the length of the maximum-length common subsequence of X and Y.
The program input is from a text file. Each data set in the file contains two strings representing the given sequences. The sequences are separated by any number of white spaces. The input data are correct. For each set of data the program prints on the standard output the length of the maximum-length common subsequence from the beginning of a separate line.
Input

abcfbc abfcab
programming contest
abcd mnp

Output

4
2
0

思路:这是一道LCS模板题,lcs就是longest common subsequence,最长公共子序列。要求其长度,当然可以暴力跑,但肯定会超时,所以可以用dp来做。两个字符串遍历一遍,遇到相同的就+1,否则就取最大值。

#include<bits/stdc++.h>
using namespace std;
#define debug cout<<"OK"<<endl
typedef long long ll;
const int maxn=1e3+1;
const int mod=1e9+7;
int n,len1,len2,dp[maxn][maxn];
string s1,s2;
void lcs(int a,int b)
{
    for(int i=1;i<=a;i++)//O(nm)
        for(int j=1;j<=b;j++)
        {
            if(s1[i-1]==s2[j-1])//有相同的就+1
                dp[i][j]=dp[i-1][j-1]+1;
            else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);//否则就取上一个得到答案中的最大值
        }
}
int main()
{
    ios::sync_with_stdio(false);//加速
    cin.tie(0),cout.tie(0);
    while(cin>>s1)
    {
        cin>>s2;
        len1=s1.length();
        len2=s2.length();
        memset(dp,0,sizeof(dp));
        lcs(len1,len2);
        cout<<dp[len1][len2]<<endl;;
    }
    return 0;
}

The company “21st Century Fruits” has specialized in creating new sorts of fruits by transferring genes from one fruit into the genome of another one. Most times this method doesn’t work, but sometimes, in very rare cases, a new fruit emerges that tastes like a mixture between both of them.
A big topic of discussion inside the company is “How should the new creations be called?” A mixture between an apple and a pear could be called an apple-pear, of course, but this doesn’t sound very interesting. The boss finally decides to use the shortest string that contains both names of the original fruits as sub-strings as the new name. For instance, “applear” contains “apple” and “pear” (APPLEar and apPlEAR), and there is no shorter string that has the same property.

A combination of a cranberry and a boysenberry would therefore be called a “boysecranberry” or a “craboysenberry”, for example.

Your job is to write a program that computes such a shortest name for a combination of two given fruits. Your algorithm should be efficient, otherwise it is unlikely that it will execute in the alloted time for long fruit names.
Input
Each line of the input contains two strings that represent the names of the fruits that should be combined. All names have a maximum length of 100 and only consist of alphabetic characters.

Input is terminated by end of file.
Output
For each test case, output the shortest name of the resulting fruit on one line. If more than one shortest name is possible, any one is acceptable.
Sample Input

apple
peach
ananas
banana
pear
peach

Sample Output

appleach
bananas
pearch

#include<bits/stdc++.h>
using namespace std;
#define debug cout<<"OK"<<endl
typedef long long ll;
const int maxn=1e3+1;
const int mod=1e9+7;
int n,len1,len2,dp[maxn][maxn];
string s1,s2,ans;
void LCS(int a,int b)
{
    for(int i=1;i<=a;i++)
        for(int j=1;j<=b;j++)
        {
            if(s1[i-1]==s2[j-1])
                dp[i][j]=dp[i-1][j-1]+1;
            else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
        }
}
void print()
{
    int i=len1,j=len2;
    ans="";
    while(dp[i][j])
    {
        if(s1[i-1]==s2[j-1])
            ans+=s1[--i],j--;
        else if(dp[i-1][j]>dp[i][j-1])ans+=s1[--i];
        else if(dp[i-1][j]<=dp[i][j-1])ans+=s2[--j];
    }
    while(i!=0)
        ans+=s1[--i];
    while(j!=0)
        ans+=s2[--j];
    reverse(ans.begin(),ans.end());
    cout<<ans<<endl;
    return;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    while(cin>>s1)
    {
        cin>>s2;
        memset(dp,0,sizeof(dp));
        len1=s1.length();
        len2=s2.length();
        LCS(len1,len2);
        print();
    }
    return 0;
}

二.LIS最长上升子序列

Nowadays, a kind of chess game called “Super Jumping! Jumping! Jumping!” is very popular in HDU. Maybe you are a good boy, and know little about this game, so I introduce it to you now.
在这里插入图片描述

The game can be played by two or more than two players. It consists of a chessboard(棋盘)and some chessmen(棋子), and all chessmen are marked by a positive integer or “start” or “end”. The player starts from start-point and must jumps into end-point finally. In the course of jumping, the player will visit the chessmen in the path, but everyone must jumps from one chessman to another absolutely bigger (you can assume start-point is a minimum and end-point is a maximum.). And all players cannot go backwards. One jumping can go from a chessman to next, also can go across many chessmen, and even you can straightly get to end-point from start-point. Of course you get zero point in this situation. A player is a winner if and only if he can get a bigger score according to his jumping solution. Note that your score comes from the sum of value on the chessmen in you jumping path.
Your task is to output the maximum value according to the given chessmen list.
Input
Input contains multiple test cases. Each test case is described in a line as follow:
N value_1 value_2 …value_N
It is guarantied that N is not more than 1000 and all value_i are in the range of 32-int.
A test case starting with 0 terminates the input and this test case is not to be processed.
Output
For each case, print the maximum according to rules, and one line one case.
Sample Input

3 1 3 2
4 1 2 3 4
4 3 3 2 1
0

Sample Output

4
10
3

解析都在代码里了

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+10;
int dp[maxn],n,a[maxn],ans;
int main()
{
    while(cin>>n)
    {
        if(n==0)break;
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++)
            cin>>a[i];
        dp[1]=a[1];
        for(int i=2;i<=n;i++)
        {
            int temp=0;
            for(int j=1;j<i;j++)
            {
                if(a[j]<a[i])//从这个数前面所有小于它的数中选取和最大的
                    temp=max(dp[j],temp);
            }
            dp[i]=temp+a[i];//dp[i]代表所有小于第i个数的递增数的和的最大值
        }
        ans=0;
        for(int i=1;i<=n;i++)ans=max(dp[i],ans);
        cout<<ans<<endl;
    }
    return 0;
}
//数据3 1 3 2
//dp[i]的值1 4 3

发布了40 篇原创文章 · 获赞 51 · 访问量 2514

猜你喜欢

转载自blog.csdn.net/weixin_45697774/article/details/103904191