【动态规划】LCS算法:求两字符串最大公共子序列/删除字符使成为回文串
来源:互联网 发布:怎么面试美工 编辑:程序博客网 时间:2024/06/05 07:42
问题描述:给定一个字符串s,你可以从中删除一些字符,使得剩下的串是一个回文串。如何删除才能使得回文串最长呢?输出需要删除的字符个数。
例如:输入:google 输出:2
思路:回文串通常可以用逆序的方式寻找思路。例如字符串google逆序后elgoog,字符串alibaba逆序后ababila,可以发现求回文串的问题可以转换成求两个字符串的最大公共子序列的问题(序列可以不连续)。
需要删除的长度 = 字符串的长度 - 字符串与逆序字符串的最大公共子序列的长度
问题描述:求两个字符串的最大公共子序列(LCS)(序列可以不连续)
例如:alibaba和ababila的最大公共子序列为ababa
思路:对于字符串a和字符串b,长度分别为m,n,先考虑他们的最后一个字符。
(1)如果相等,说明最后一个字符一定可以是最大公共子序列的最后一位(这里使用可以的意思是:有多解),那么我们可以先丢弃这最后一位,求解字符串a.substr(0, m-2)和b.substr(0, n-2)的最大公共子序列。a.substr(x,y)表示截取字符串a的x到y下标之间的部分
(2)如果不相等,则最大子序列一定在一下两者之间:a.substr(0, m-2) 和 b.substr(0, n-1)(丢弃a的最后一位)a.substr(0, m-1) 和 b.substr(0, n-2)(丢弃b的最后一位)
递归方法
这样就可以用递归求解(只求解长度)。
int LCS_Length(string str1, string str2, int n1, int n2){<span style="white-space:pre"></span>if(n1==0 || n2==0)<span style="white-space:pre"></span>return 0;<span style="white-space:pre"></span>if(str1[n1-1]==str2[n2-1])<span style="white-space:pre"></span>return 1+LCS_Length(str1.substr(0,n1-1),str2.substr(0,n2-1),n1-1,n2-1);<span style="white-space:pre"></span>else<span style="white-space:pre"></span>{<span style="white-space:pre"></span>int a=LCS_Length(str1,str2.substr(0,n2-1),n1,n2-1);<span style="white-space:pre"></span>int b=LCS_Length(str1.substr(0,n1-1),str2,n1-1,n2);<span style="white-space:pre"></span>return max(a,b);<span style="white-space:pre"></span>}}
动态规划法
但是每次递归,都要对前面的子字符串的最大公共子序列重新计算一遍,效率很低。因此可以用动态规划的方法。
动态规划法利用了一个矩阵vec,矩阵中第i行第j列表示,字符串前i个字符与字符串前j个字符的最大公共子序列,因此第0行与第0列全部元素为0。如图所示。
其余元素均可根据其上方、左上方、左方的三个数推断出来。规则来自上述的思路(1)(2):判断当前vec[i][j]对应的行列字符是否相等,如果相等,则取左上方加1 vec[i][j] = vec[i-1][j-1] + 1;如果不等,则取上方vec[i-1][j]和 左方 vec[i][j-1]的最大值。
例如:图2中红色的2:判断行列字符都是b,因此为左上角1+1得到2;图2中绿色的3:判断行列字符b和a不等,取上方和左方的最大值3.
整个矩阵求出之后,右下角的元素值就是字符串a和b的最大公共子序列的长度了。
接下来,从右下角开始回溯,以求最大公共子序列
根据上文的推到思路可以找到回溯思路,从右下角开始
(1)如果当前元素等于其左上角加1,那么当前元素行列相等,是公共子序列中的一个,保存下来。
(2)如果当前元素来自上方或左方,当前元素不保存。
按照此思路,从5开始,形成如图所示的回溯路线,只有斜向上的箭头对应的行元素纳入最大公共子序列,求得最大公共子序列为ababa。
完整代码:
#include <iostream>#include <string>#include <algorithm>#include <vector>using namespace std; int LCS_Length(string str1,string str2,vector<vector<int>> &vec)//求长度和LCS矩阵{int i,j;int n1=str1.size();int n2=str2.size(); for(i=0;i<n2+1;i++)//第0行置0 vec[0][i] = 0; for(i= 0;i<n1+1;i++)//第0列置0 vec[i][0] = 0; for(i=1;i<=n1;i++) { for(j=1;j<=n2;j++) { if(str1[i-1]==str2[j-1]) vec[i][j]=vec[i-1][j-1]+1; else vec[i][j]=max(vec[i][j-1],vec[i-1][j]); } } return vec[n1][n2];}void LCS(string str,vector<vector<int>> vec,int i,int j)//回溯,求LCS{ if(i==-1||j==-1) return; if(vec[i+1][j+1]==vec[i][j+1]) LCS(str,vec,i-1,j); else if(vec[i+1][j+1]==vec[i+1][j]) LCS(str,vec,i,j-1); else { LCS(str,vec,i-1,j-1);cout<<str[i]; } }int main(){ string str; while(cin>>str){int lcslen; int len = str.size(); string str_reverse = str; reverse(str_reverse.begin(),str_reverse.end());vector<vector<int>> vec(len+1);//创建动态规划矩阵for(int i = 0; i < len+1; ++i)vec[i].assign(len+1,-1); lcslen=LCS_Length(str,str_reverse,vec);cout<<"最大公共子序列长度:"<<lcslen<<endl;cout<<"构成回文串要删除的字符个数:"<<len-lcslen<<endl;cout<<"最大公共子序列(即构成的回文串)为:";LCS(str,vec,len-1,len-1);cout<<endl; } return 0;}
- 【动态规划】LCS算法:求两字符串最大公共子序列/删除字符使成为回文串
- 求两字符串最长公共子序列LCS的应用—删除字符串的字符剩下回文串
- LCS算法[动态规划 最长公共子序列]求回文串
- 最长回文子序列:字符串反转+动态规划,最长公共子序列LCS算法
- 动态规划 字符串最大公共子序列以及最大公共子串问题LCS
- 【动态规划】LCS算法:求两字符串最大公共字符串(连续)
- 动态规划问题--最长公共子序列(LCS)问题--删除一些字符使得剩下的是一个回文子串
- 最长公共子序列LCS和最长回文子序列的动态规划算法
- 插入最少的字符使字符串成为回文串 <最长公共子序列 + 回文>
- Poj1159 Palindrome(动态规划DP求最大公共子序列LCS)
- 【算法之动态规划(三)】动态规划算法之:最长公共子序列 & 最长公共子串(LCS),字符串相似度算法
- 动态规划_求最长公共子序列LCS
- 动态规划经典之求最长公共子序列LCS
- 动态规划求最长公共子序列LCS
- 算法导论——动态规划之最长公共子序列(LCS)和最长回文子序列(LPS)
- 动态规划算法之:最长公共子序列 & 最长公共子串(LCS)
- 动态规划算法之:最长公共子序列 & 最长公共子串(LCS)
- 动态规划算法之:最长公共子序列 & 最长公共子串(LCS)
- (2016 Multi) physics hdu 5826
- Lock Mode Type 之 Optimistic 使用场景
- java旅行第二站--javaSE第9天--服务器端与客户端之间互相通信
- PHP上传类 图片上传 upload class实现image crop resize 缩略图
- leetcode-100. Same Tree
- 【动态规划】LCS算法:求两字符串最大公共子序列/删除字符使成为回文串
- shell脚本几个小作业(含答案)
- 在CentOS安装PHP5.6
- Java类
- {模板}Gcd及ExGcd
- 常用命令1
- SDUTACM 树-堆结构练习——合并果子之哈夫曼树
- oracle数据库服务器02
- java OO基础 总结