ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

最长公共子序列(计数问题)

2021-06-03 20:03:46  阅读:183  来源: 互联网

标签:s2 s1 长度 序列 计数问题 最长 dp


题意:
字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列。令给定的字符序列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的一个子序列。对给定的两个字符序列,求出他们最长的公共子序列长度,以及最长公共子序列个数。

一.求最长公共子序列的长度(基础LCS问题)
首先从动态规划的角度来考虑:
所有问题被分解为子问题,子问题套子问题。
1.确定研究状态:最长公共子序列的长度
那么每一层的问题就转化求该层的***最长公共子序列的长度***。
2.确定研究问题的划分层次(如何划分子问题)。(循环变量所代表的意义及状态边界问题):当s1和s2的长度均为1时,结果是很容易判断的,所有很自然的想到用长度来对问题进行划分。长度小的子问题被包含在长度大的子问题中作为数据被调用。——>用两层循环(i,j)来进行。
3.确定状态转移方程:(重中之重,因为状态转移方程是否正确决定难题的攻克与否)同时,也是解决问题的核心。那么对于长度为i的s1和长度为j的s2。该状态下的子问题结果:dp[i][j]=?。
可以知道的是:分情况讨论:
(1).当S1[i]==S2[j]时,dp[i][j]可有三个来源:
当第i个和第j个参与最长公共子序列①:dp[i-1][j-1]+1;
当第i个和第j个不参与最长公共子序列②:dp[i-1][j]和dp[i][j-1]。
因为:dp[i-1][j-1]<=dp[i][j-1]<=dp[i-1][j-1]+1 所以,最终的答案是:
dp[i][j]=dp[i-1][j-1]+1。
(2).当S1[i]!=S2[j]时,S1[i]与S2[j]不可能同时入选最长公共子序列,所以dp[i][j]只能从dp[i-1][j]和dp[i][j-1]中推得结果。根据 最长 的要求,和dp所储存的内容( s1的前i个字符组成的串和 s2的前j个字符组成的串所能形成的最长公共子序列的长度)得到:dp[i][j]=max(dp[i-1][j],dp[i][j-1]).
4.构造关键代码:

for (i=1;i<=s1.length();i++) 
for (j=1;j<=s2.length();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])

二.求最长公共子序列的方案数:
依照动态规划的过程:
1.选定分析状态——最长公共子序列的方案数
2.分析研究问题的划分层次——(子问题和最终最初一步)
显而易见:当长度为1时结果显而易见。
所以依照长度去研究子问题得到的循环变量以及划分这阶段的依据。
3.分析状态转移方程。
当循环变量至s1[i]和s2[j]时,状态转移方程为:
(首先我们能够得知,g[i][j]来源于g[i-1][j-1],g[i][j-1],g[i-1][j])
(1) 当s1[i]==s2[j]时,
(①即当s1[i]与s2[j]做出贡献时,方案数与g[i-1][j-1]一致) g[i][j]+=g[i-1][j-1] 。
(②即当s1[i]与s2[j]不做出贡献时),需要判定dp[i-1][j],dp[i][j-1]与dp[i][j]是否相等(出现不相等则意味着最长公共子序列的长度改变,那么方案数就不能被记入)。
而同时因为 dp[i][j]=dp[i-1][j-1]+1所以dp[i][j]不一定会等于dp[i-1][j]或dp[i][j-1]。
所以需要用if语句判断,如果相等再将g[i-1][j]和g[i][j-1]的方案书加上。
即关键代码:

if (s1[i]==s2[j])
        {
            dp[i][j]=dp[i-1][j-1]+1;
            if ((dp[i][j]==1)&&(g[i][j]==0)) ++g[i][j];
            (因为方案开头需要新增方案数,即从0到1的改变)
            g[i][j]+=g[i-1][j-1];
            (即当s1[i]与s2[j]做出贡献时,这种情况下g[i-1][j-1]被记入g[i][j])
            if (dp[i][j]==dp[i][j-1]) g[i][j]+=g[i][j-1];
            (倘若长度相等则意味着s1[i]与s2[j]没有被同时算入子序列时,
            仍然有长度等于dp[i][j]的方案,数量等于g[i][j-1],此时g[i][j-1]也要被记入g[i][j])
            if (dp[i][j]==dp[i-1][j]) g[i][j]+=g[i-1][j];
            (倘若长度相等则意味着s1[i]与s2[j]没有被同时算入子序列时,
            仍然有长度等于dp[i][j]的方案,数量等于g[i-1][j],此时g[i-1][j]也要被记入g[i][j])
            g[i][j]=g[i][j]%p;(数量过大时,需要取模)
            /*
            此处需要解释,g[i][j-1]和g[i-1][j]的方案数指代的方案完全不重合
            (s1[i]或s2[j]必然参与构成最长公共子序列)。
            因为如果dp[i][j]==dp[i-1][j]==dp[i-1][j-1]+1。
            
            假设:存在s1[i]与s2[j]均不参与组成最长公共子序列同时有合法方案
            则有dp[i-1][j]==dp[i-1][j-1]或dp[i][j-1]==dp[i-1][j-1]。
            
            因为在s1[i]与s2[j]均不参与构成时仍有合法方案,
            则所有参与构成最长公共子序列的元素均来自于s1的前i-1个字符和s2的前j-1个字符
            此时又有在s1[i]与s2[j]均不参与构成时仍有合法方案,
            则有dp[i][j]==dp[i-1][j]==dp[i-1][j-1] ==>矛盾
            
            反证:g[i][j-1]和g[i-1][j]的方案数指代的方案完全不重合
            */
        }

(2) 当s1[i]!=s2[j]时,g[i][j]只有两个来源①g[i-1][j]②g[i][j-1] (因为s1[i]与s2[j] 不能同时参与当前的最长公共子序列的组成)。
①s1[i]与s2[j]均不做出贡献时:(dp[i][j]==dp[i-1][j-1])
dp[i][j]==dp[i-1][j-1];g[i][j]+=g[i-1][j-1]。(因为均不做出贡献,所有参与构成最长公共子序列的元 素均来自于s1的前i-1个字符和s2的前j-1个字符)
②s1[i]做出贡献,而s2[j]不做出贡献时:(dp[i][j]==dp[i][j-1])
dp[i][j]==dp[i][j-1],同时确定g[i][j-1]包含 s1[i]做出贡献与s1[i]不做出贡献两种情况。
所以g[i][j]+=(g[i-1][j]-g[i-1][j-1])。
③s2[j]做出贡献,而s1[i]不做出贡献时:(dp[i][j]==dp[i-1][j])
dp[i][j]==dp[i][j-1],同时确定g[i-1][j]包含 s2[j]做出贡献与s2[j]不做出贡献两种情况。
所以g[i][j]+=(g[i][j-1]-g[i-1][j-1])。
则有关键代码:

if (s1[i]!=s2[j])
{
    dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
    if (dp[i][j-1]==dp[i][j]) g[i][j]+=g[i-1][j];
    if (dp[i-1][j]==dp[i][j]) g[i][j]+=g[i][j-1];
    if (dp[i-1][j-1]==dp[i][j]) g[i][j]-=g[i-1][j-1];//其实在s1[i]==s2[j]中也因当有这一步,
    //但是因为dp[i][j]==dp[i-1][j-1]+1;所以这个判断语句可以省略。 
}

附:当字符串的长度达到5000+时,内存需求非常大,这样我们需要使用滚动数组。
我们发现,每一次dp[i]与g[i]的答案都只与第i层和第i-1层的结果有关,所以前i-2的数据都不可能在被使用,运用滚动数组可以优化空间。

(确定状态(最后一步与子问题)=>递归调用=>保留递归结果优化解决方案,状态转移方程,初始条件和边界情况,计算顺序)

原文链接:https://blog.csdn.net/qq_35851866/article/details/104102182

标签:s2,s1,长度,序列,计数问题,最长,dp
来源: https://www.cnblogs.com/kayiko/p/14846604.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有