2. 动态规划求解最长公共子串的问题
2.1 问题描述:
Description 找出2个给定字符串中最长的公共子字符串
Prototype void vProcTaskToCoreByLayerAndTime(unsigned int Layer,unsigned int* pDuration)
Input Param char* pSrcStr1, char* pSrcStr2: 给定的2个字符串
Output Param char* pSubStr : 从给定的字符串中找到的最长公共子串
Return Value
2.2 问题分析
最长公共子串的问题具有最优子结构的性质,注意最长公共子序列的下标不需要一致,否则就成了字符串匹配了。
思路:
首先,定义问题的规模,显然因为有两个不同长度的串,无法用统一的串长来定义问题规模,故用一个偶pair来表示问题的规模(Xm,Yn),且从后往前找,Xm代表从串1的1到m的完全子串,Yn来代表从串2的1到n的完全子串
然后,分析问题的最优子结构的性质,若(Xm,Yn)【初始化为XN1和YN2,N1和N2分别为两个串的长度】的最长公共子序列为Z[t],则Z去掉最后一个元素Z[t-1]也必然是(Xm-1,Yn-1)或(Xm-1,Yn)或(Xm,Yn-1)的最长公共子串。这里问题规模的降低分了三种情况。
最后,假设底层问题都求解好了,用底层即规模小的问题来定义大规模的问题的解法即定义问题的递归的解的形式。
情况1:若Xm=Yn,则Z{ (Xm,Yn)}=Z{Xm-1,Yn-1}+1
情况2.1:若Xm!=Yn且Xm-1=Yn且Xm-1=Yn包含在LCS里,则Z{ (Xm,Yn)}=Z{Xm-1,Yn}
情况2.2:若Xm!=Yn,且Xm=Yn-1且Xm=Yn-1包含在LCS里,则Z{ (Xm,Yn)}=Z{Xm,Yn-1}
情况2.3:不满足上述情况,则Z{ (Xm,Yn)}=Z{Xm-1,Yn-1}或Z{Xm-1,Yn}或Z{Xm-1,Yn}这些结论肯定是错误的,因为我们无法判断到底是xm-1或xm或ym或ym-1是否属于LCS。
已知{x[i],y[j]}的最优值为Z{x[i],y[j]},则无法确定x[i]==y[j-1]是否属于这个LCS序列,因为说不定y[1:j]和x[1:i-1]构成最优解,也说不定y[1:j]和x[1:i-2]构成最优解,也说不定。。总之,这样说不定的事情就多了。
Btw,动态规划的一个基本思想就是只考虑当前层和与当前层最近邻的一层(否则思考空间超过了人类大脑的栈的长度),即假设最近邻的下层问题已经求好了。
因此上面说不定的问题其实可以归为两种情况,要么xi不属于lcs要么yj不属于lcs。
而xi不属于lcs这个命题等价于Z{x[i],y[j]}= Z{x[i-1],y[j]}
yj不属于lcs这个命题等价于Z{x[i],y[j]}= Z{x[i],y[j-1]}
这两个命题概括了所有的说不定,到底哪个命题对呢?比一比就知道了,所以
Z{x[i],y[j]}=max{ Z{x[i-1],y[j]}, Z{x[i],y[j-1]}}
2.3 问题求解:
2.3.1数据结构:令数组s[m][n]表示{Xm,Yn}情况下的LCS的长度,
if(x[m]==y[n]) s[m][n]=s[m-1][n-1]+1;
elseif(s[m][n-1]>s[m-1][n]) s[m][n]=s[m][n-1];
else s[m][n]=s[m-1][n-1];
2.3.2 初始化:显然s[1][1]由x[1]=y[1]?来判定.是否有s[1][1]的值就足够了呢?遍历时需要s[m][n]=s[m-1][n-1],则s[3][1]=s[3][0]的情况如何处理呢?
故还要包上s[i][0]=0和s[0][j]=0的初始条件,且这两个条件就包含了s[1][1]的取值。
2.3.3 外层与内层循环
外层循环递增问题的规模,这里的问题规模应该是x[]和y[]数组的下标的增加,但是有俩下标,如何增加呢?即如何从s[1][1]到s[N1][N2]呢?
s[1][i]循环为遍历{x[1],y[1:i]}比较的情况在{{x[1],y[1:i-1]}}已经比较过的情况下是可行的。因此外层循环为s[i][j],先循环i再循环j(在这个问题里先后循环没有关系)
2.3.4 最优解的获得
上述的遍历只能求解最优值,最优解需要通过递归来实现,因此需要存储达到最优解的中间步骤,
Backtrace(i,j)
递归出口:if(i==1&&j==1)
{
If(x[1]==y[1]) cout<<”x[1]”;
return;
}
if(x[i]==y[i])//这种情况肯定x[i]==x[j]肯定是属于最优LCS里的元素,否则,把该元素加入到LCS中就增加了LCS的长度,与LCS最长的假设相悖。
{
backtrace(i-1,j-1);
cout<<”x[i]”;输出最优序列
}
elseif(s[i][j-1]> s[i-1][j]) Backtrace(i,j-1);
Else Backtrace(i-1,j-1);