二维dp

1.Common Subsequence
Description

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.
Input

The program input is from the std input. Each data set in the input contains two strings representing the given sequences. The sequences are separated by any number of white spaces. The input data are correct.
Output

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.
Sample Input

abcfbc abfcab
programming contest
abcd mnp
Sample Output

4
2
0
思路在代码中
ac代码:

#include
#include
#include
#include
#include
using namespace std;
char s1[1010],s2[1010];
int len1,len2,dp[1010][1010];
void DP(){
    for(int i=1;i<=len1;i++)
    for(int j=1;j<=len2;j++){
        if(s1[i]==s2[j])
            dp[i][j]=dp[i-1][j-1]+1;
        else
            dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
    }//这里面是关键。
    //当两字符串某位置上字符相等的时候,此刻最长公共子序列是上一个状态(i-1,j-1)+1.
    //当不相等的时候,此刻最长公共子序列是dp[i-1][j]和dp[i][j-1])中最大的。
    //这两个状态转移最关键。
    cout<<dp[len1][len2]<<endl;
}
int main()
{
    while(~scanf("%s",s1+1)){//~就相当与!=EOF,s1+1让字符串从下标为一的位置输入。
    //注意下面strlen()也要加一
        scanf("%s",s2+1);
        memset(dp,0,sizeof(dp));
        len1=strlen(s1+1);
        len2=strlen(s2+1);
        DP();
    }
    return 0;
}

2.最长公共子序列Lcs
给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的)。
比如两个串为:

abcicba
abdkscab

ab是两个串的子序列,abc也是,abca也是,其中abca是这两个字符串最长的子序列。
Input
第1行:字符串A 第2行:字符串B (A,B的长度 <= 1000)
Output
输出最长的子序列,如果有多个,随意输出1个。
Sample Input
abcicba
abdkscab
Sample Output
abca
ac代码:

#include
#include
#include
using namespace std;
char s1[1010],s2[1010],c[1010];
int dp[1010][1010],len1,len2;
void DP(){
    for(int i=1;i<=len1;i++)
        for(int j=1;j<=len2;j++)
    {
        if(s1[i]==s2[j])
            dp[i][j]=dp[i-1][j-1]+1;
        else
            dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
    }


}
void out(){
    int k=0;
    int x=len1,y=len2;
    while(x!=0&&y!=0){
        if(s1[x]==s2[y]){
            c[++k]=s1[x--];
            y--;//需要注意.
            //为什么y也需要减呢,举个反例:s1:aaa,s2:a;思考一下就明白了。
        }
        else if(dp[x][y-1]>dp[x-1][y])
               y--;
        else if(dp[x][y-1]<=dp[x-1][y])//这个也让我迷惑了很长时间。
        //当时我在想,当等于的时候应该可以直接x--同时y--,这里有个例子否定
        //这个想法,:s1:abcicba  s2:abdkscab,这个例子最长公共子序列有两个abcb和abca,因此在
        //同时x--和y--的时候,正好讲个子序列最后的都毁了变成了abc.
        //还有一个说明这种想法不行:在找子序列的时候是通过推dp[][]的方法反推出来的,
        //因此应该像推dp[][]的时候一样,x和y,一个一个遍不能一起变。
            x--;
    }
    for(int i=k;i>=1;i--)
        cout<<c[i];
}
int main()
{
    cin>>s1+1;
    cin>>s2+1;
    len1=strlen(s1+1);
    len2=strlen(s2+1);
    DP();
    out();



    return 0;
}

Advanced Fruits
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
ac代码:

#include
#include
#include
#include
#include
using namespace std;
const int maxn=1010;
int dp[maxn][maxn],len1,len2;
char s1[maxn],s2[maxn],c[maxn];
void DP(){
for(int i=1;i<=len1;i++)
for(int j=1;j<=len2;j++){
    if(s1[i]==s2[j])
        dp[i][j]=dp[i-1][j-1]+1;//别忘了加一
    else
        dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
}
void out(){
    int k=0;
    int i=len1,j=len2;
    while(dp[i][j]){//为什么这里的循环要通过判断dp[i][j]是否为零呢?
    //因为本题中s1与s2不等于的部分也要输入,而在第一个等于的字符之前中dp[][]
    //都是零,但是当p[][]时零的时候,下面的关系式不能确定式s1前面的长,还是s2前面
    //的长,因此当dp[i][j]==0时我们在这个循环外面,在进行相关操作。
        if(s1[i]==s2[j]){
            c[++k]=s1[i--];
            j--;
        }
        else if(dp[i-1][j]>dp[i][j-1])
            c[++k]=s1[i--];
        else if(dp[i-1][j]<=dp[i][j-1])//我仔细想了想当这里是等于的时候就相当于有多种答案,
        //一次这里j--或者i--都行。
            c[++k]=s2[j--];
    }
    while(i!=0){
        c[++k]=s1[i--];
    }
    while(j!=0){
        c[++k]=s2[j--];
    }
    for(int t=k;t>=1;t--)
        cout<<c[t];
    cout<<endl;
}
int main()
{
       while(~scanf("%s",s1+1)){
        scanf("%s",s2+1);
        memset(dp,0,sizeof(dp));//注意这题要求多组输入,因此初始化dp[][].
        len1=strlen(s1+1);
        len2=strlen(s2+1);
        DP();
        out();
       }
       return 0;
}

思路(感谢作者):https://blog.csdn.net/lxt_Lucia/article/details/81209962
编辑距离:
1183 编辑距离(最短编辑距离)
编辑距离,又称Levenshtein距离(也叫做Edit Distance),是指两个字串之间,由一个转成另一个所需的最少编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。
例如将kitten一字转成sitting:
sitten (k->s)
sittin (e->i)
sitting (->g)
所以kitten和sitting的编辑距离是3。俄罗斯科学家Vladimir Levenshtein在1965年提出这个概念。
给出两个字符串a,b,求a和b的编辑距离。
输入
第1行:字符串a(a的长度 <= 1000)。
第2行:字符串b(b的长度 <= 1000)。
输出
输出a和b的编辑距离
输入样例

kitten
sitting
输出样例
3
**思考:**这一题一开始我想的是像前面几个题一样先像上面一样dp再用函数写类似上面的out()函数,用s记操作步骤,没写出来。
现在看来这种dp的结果是数的话就在关键在dp时的步骤是关键和不同。
如果是字符的话那就是out函数部分不同、关键(菜鸡个人感觉)。
ac代码:

#include
#include
#include
using namespace std;
const int maxn=1010;
int dp[maxn][maxn],len1,len2;
char s1[maxn],s2[maxn];
void DP()
{
    for(int i=1;i<=len1;i++)
        dp[i][0]=i;
    for(int i=1;i<=len2;i++)
        dp[0][i]=i;
    for(int i=1;i<=len1;i++)
        for(int j=1;j<=len2;j++)
    {
        if(s1[i]==s2[j])
        dp[i][j]=dp[i-1][j-1];//相同的话就不用操作。
    else dp[i][j]=min(dp[i-1][j-1]+1,min(dp[i][j-1]+1,dp[i-1][j]+1));
    //不同的话就要操作,这里有三种操作替换、插入和删除。
   //分别是dp[i-1][j-1]+1、dp[i][j-1]+1和dp[i-1[[j]+1。
    //这三个应该跟据dp关系写,容易理解。
    }
}
int main()
{
    cin>>s1+1;
    cin>>s2+1;
    len1=strlen(s1+1);
    len2=strlen(s2+1);
    DP();
    cout<<dp[len1][len2];
    return 0;
}

你可能感兴趣的:(二维dp)