算法提高2——Leecode 1092. 最短公共超序列

题目如下

解题思路

解法1:动态规划

动态规划回顾

  • 什么是动态规划?
  • 动态规划的核心思想
  • 一个例子走进动态规划
  • 动态规划的解题套路

动态规划的含义

动态规划(英语:Dynamic programming,简称 DP),是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划常常适用于有重叠子问题和最优子结构性质的问题。

★ dynamic programming is a method for solving a complex problem by breaking it down into a collection of simpler subproblems.”

以上定义来自维基百科,看定义感觉还是有点抽象。简单来说,动态规划其实就是,给定一个问题,我们把它拆成一个个子问题,直到子问题可以直接解决。然后呢,把子问题答案保存起来,以减少重复计算。再根据子问题答案反推,得出原问题解的一种方法。

★ 一般这些子问题很相似,可以通过函数关系式递推出来。然后呢,动态规划就致力于解决每个子问题一次,减少重复计算,比如斐波那契数列就可以看做入门级的经典动态规划问题。”

核心思想动态规划最核心的思想,就在于拆分子问题,记住过往,减少重复计算

我们来看下,网上比较流行的一个例子:

        A : "1+1+1+1+1+1+1+1 =?"
        A : "上面等式的值是多少"
        B : 计算 "8"
        A :    在上面等式的左边写上 "1+" 呢?
        A :   "此时等式的值为多少"
        B :   很快得出答案 "9"
        A :   "你怎么这么快就知道答案了"
        A :   "只要在8的基础上加1就行了"
        A :   "所以你不用重新计算,因为你记住了第一个等式的值为8!动态规划算法也可以说是 '记住求过的解来节省时间'"​​​​

动态规划的解题套路

什么样的问题可以考虑使用动态规划解决呢?

★ 如果一个问题,可以把所有可能的答案穷举出来,并且穷举出来后,发现存在重叠子问题,就可以考虑使用动态规划。”

比如一些求最值的场景,如最长递增子序列、最小编辑距离、背包问题、凑零钱问题等等,都是动态规划的经典应用场景。

动态规划的解题思路

动态规划的核心思想就是拆分子问题,记住过往,减少重复计算。 并且动态规划一般都是自底向上的,因此到这里,基于青蛙跳阶问题,我总结了一下我做动态规划的思路:

  • 穷举分析
  • 确定边界
  • 找出规律,确定最优子结构
  • 写出状态转移方程

代码

dp[0][0][...] = 边界值
for(状态1 :所有状态1的值){
    for(状态2 :所有状态2的值){
        for(...){
          //状态转移方程
          dp[状态1][状态2][...] = 求最值
        }
    }
}

参考文献:

动态规划部分简介 - OI Wiki (oi-wiki.org)

看一遍就理解:动态规划详解 - 知乎 (zhihu.com)

思路和算法

即只考虑在前一个情况“已有的公共序列”基础上的变化。

step1:递归公式(第一步总的先来构建dp矩阵)

  1. 当X(Y)至少有一个序列为空时:
    此时,最短公共超序列z一定要包含X(Y)全部字符,而求最短公共超序列,于是最短公共超序列就是X(Y)本身;(代码中即是先初始化0行0列)
    1. Y为空时:dp(i,0)=i (1<=i<=m)
    2. X为空时:dp(0,j)=j (1<=j<=n)
  2. 当X[m]=Y[n]时:
    此时,该字母一定包含在最短公共超序列中,相等就只需要加一个就好了。(比如x中是a,y中也是a,最短公共超序列中只需要加一个a就行了);
    1. dp(m,n)= dp(m-1,n-1)+1
  3. 当X[m] != Y[n]时:
    当x[m](y[n])为最短公共超序列中需要新添加的一个字符时(x出现了一个从来没出来的字符);(注:下面的情况1和情况2都可计算出可能出现的超序列,但是为了最终选择最短超序列,于是选择他们中的最小者,进行当作这一步的基础)
    1. dp[i][j] = dp[i-1][j] + 1
    2. dp[i][j] = dp[i][j-1] + 1
  4. 举例:str1=AABXFGA,str2=ABAAXGF;

step2:待整理?开始在dp矩阵中选择执行路线作为最终的执行地图,即序列;看代码可以理解为反向计算取出,dp[i][j]是由谁算出来的,于是便判断新增加进公共超序列的字符来自于谁(逆向计算该位置减一是得到上方还是左方,上方则来自于横轴序列,左方则来自于纵轴序列,来自横轴序列时,则向上一行,相当于改变纵轴序列,毕竟纵轴序列没有影响当前的这个新增的字符。如果a[i]和b[j]相同,则该位置计算斜上方的那个[i-1][j-1]),然后把新的字符加进来~

代码1

#include <stdio.h>
#include <string.h>
int max(int a,int b)
	{
		if(a>b)
			return a;
		else return b;
	}
int min(int a,int b)
	{
		if(a<b)
			return a;
		else return b;
	}
int dp[105][105],len1,len2,cou=0;
char a[105],b[105],c[105];	//a,b用来存放输入的两个字符串,c存放最短公共超序列的一种解 
int main()
{	int i,j;
	scanf("%s",&a);
    scanf("%s",&b);
    len1=strlen(a);
	len2=strlen(b);
    for(i=strlen(a);i>0;i--)	//为了方便dp数组的操作,我将字符往后移动一位 
    	a[i]=a[i-1];
    for(i=strlen(b);i>0;i--)
    	b[i]=b[i-1];
    a[0]='0';	//随便给的值 
    b[0]='1';
    for(i=0;i<=max(len1,len2);i++)	//当某个字符串为空时,赋初值 
		dp[i][0]=dp[0][i]=i;
    for(i=1;i<=len1;i++)
    {
        for(j=1;j<=len2;j++)
        {
            if(a[i]==b[j])
                dp[i][j]=dp[i-1][j-1]+1;	//根据递推公式求dp() 
           else 
                dp[i][j]=min(dp[i-1][j],dp[i][j-1])+1;
        }
    }
	i=len1;
    j=len2;
    //开始选择路径
	while(i>0&&j>0)	//根据比较最后的值是怎么来的,计算出最短超序列的一种解 
        {
            if(dp[i][j]==dp[i-1][j-1]+1&&a[i]==b[j])	//相等时 
                {
                c[cou]=a[i];
                cou++;
                i--;
                j--;
				}
			else if(dp[i][j]==dp[i-1][j]+1&&a[i]!=b[j])//来自a 
				{
          		c[cou]=a[i];
          	  	cou++;
           		i--;
				}
            else if(dp[i][j]==dp[i][j-1]+1&&a[i]!=b[j])//来自b 
                {
                c[cou]=b[j];
                cou++;	
                j--;
				}    
        }
    while(i>0)	//输入的字符串大小不等,或者在a,b中选取的个数不相等时,若第0行或者第0列没有选完时 
    	{
    		c[cou]=a[i];
    		i--;
    		cou++;
		}
	while(j>0)
    	{
    		c[cou]=b[j];
    		j--;
    		cou++;
		}
	for(i=strlen(c)-1;i>=0;i--)	//输出其中一个最短超序列 
		printf("%c",c[i]);
	printf("\n");
    printf("\n%d",dp[len1][len2]);
    return 0;
}


参考文献:

(40条消息) 最短公共超序列(最短公共父序列)_@玉面小蛟龙的博客-CSDN博客_最短公共超序列

猜你喜欢

转载自blog.csdn.net/Mr_yangsc/article/details/126180237