zoukankan      html  css  js  c++  java
  • 最长公共子串+最长公共子序列

    两道题都可以用动态规划的方法做,只是状态转移方程不同。

    最长公共子串(注意子串是连续的)

    1、先建立一个二维数组array[str1.size()][str2.size()](全部初始化为0),初始化第一行和第一列(元素相同处置1),然后进入状态方程

    2、状态转移方程:

      if(str1[i] == str2[i])   array[i][j]=array[i-1][j-1]+1;  (左上方对角线的值加上1)

      否则无操作。

    3、最后寻找整个array中的最大值即可(因为可能有多个子串)

    示意(图中有两个公共子串,分别为"ab"和"de",长度都为2)

    程序:

     1 /*
     2 本程序说明:
     3  
     4 最长公共子串(注意空格,不要用cin,要用getline)
     5  
     6 */
     7 #include <iostream>
     8 #include <vector>
     9 #include <string>
    10 using namespace std;
    11  
    12 int largestCommentSubString(std::string str1,std::string str2){
    13     if(0 == str1.length() || 0 == str2.length())
    14         return 0;
    15  
    16     std::vector<std::vector<int> > array(str1.size(),std::vector<int>(str2.size(),0));
    17     for(size_t i=0; i< str2.size(); ++i){
    18         if(str1[0] == str2[i])
    19             array[0][i]=1;
    20     }
    21     for(size_t i=0; i< str1.size(); ++i){
    22         if(str2[0] == str1[i])
    23             array[i][0]=1;
    24     }
    25     for(size_t i=1; i< str1.size(); ++i){
    26         for(size_t j=1; j< str2.size(); ++j){
    27             if(str1[i] == str2[j]){
    28                 array[i][j]=array[i-1][j-1]+1;
    29             }
    30         }
    31     }
    32     int length=0;
    33     for(size_t i=0; i< array.size(); ++i){
    34         for(size_t j=0; j< array[0].size(); ++j){
    35             if(array[i][j]>length){
    36                 length=array[i][j];
    37             }
    38         }
    39     }
    40     return length;
    41 }
    42  
    43 int main(){
    44     std::string str1,str2;
    45     while(getline(cin,str1),getline(cin,str2)){     
    46         cout<<largestCommentSubString(str1,str2)<<endl;
    47     }
    48     return 0;
    49 }

    最长公共子序列(注意子序列可以不连续)

    1、先建立一个二维数组array[str1.size()+1][str2.size()+1](全部初始化为0),然后进入状态方程

    2、状态转移方程:

      if(str1[i] == str2[i])   array[i][j]=array[i-1][j-1]+1; (左上方对角线的值加上1)

      if(str1[i] != str2[i])   array[i][j]=max(array[i-1][j],array[i][j-1]);  (左边和上边的最大值)

    3、最后返回整个array中的最大值即可(即array右下角元素的值)

    示意(图中的公共子序列为"abde",注意我的程序是左面的和上面的相同的情况下,优先左,当然也可以是上):

     1 /*
     2 本程序说明:
     3 
     4 最长公共子序列
     5 
     6 */
     7 #include <iostream>
     8 #include <vector>
     9 #include <string>
    10 using namespace std;
    11 
    12 int findLCS(string str1,string str2) {
    13     // write code here
    14     if(0 == str1.size() || 0 == str2.size())
    15         return 0;
    16     vector<vector<int> > array(str1.size()+1,vector<int>(str2.size()+1,0));
    17     for(size_t i=1; i<= str1.size(); ++i){//注意:是小于等于
    18         for(size_t j=1; j<= str2.size(); ++j){//注意:是小于等于
    19             if(str1[i-1] == str2[j-1]){//前面填充了一行一列,因此判断i-1和j-1
    20                 array[i][j]=array[i-1][j-1]+1;
    21                 }
    22             else
    23                 array[i][j]=max(array[i-1][j],array[i][j-1]);
    24         }
    25     }
    26     return array[str1.size()][str2.size()];
    27 }
    28 
    29 int main(){
    30     string str1,str2;
    31     while(getline(cin,str1),getline(cin,str2)){
    32         cout<<findLCS(str1,str2)<<endl;
    33     }
    34     return 0;
    35 }

    如果还要进一步打印出其中一个公共子序列的话,需要用到回溯法。我们在动态规划时需要记录元素的状态,一步步回溯回去即可。

     1 /*
     2 本程序说明:
     3 
     4 最长公共子序列(加上了其中一个子序列的打印功能,回溯法)
     5 
     6 */
     7 #include <iostream>
     8 #include <vector>
     9 #include <string>
    10 using namespace std;
    11 
    12 //打印(回溯法)
    13 void printLCS(const string& str1,const string& str2,const vector<vector<string> >& flag,int i,int j,string& str)
    14 {
    15     if(0==i||0==j)
    16         return;
    17     if("left_up"==flag[i][j])
    18     {
    19         str.insert(str.begin(),str1[i-1]);//把要打印的公共字符逆序存在str中(因为回溯法从后向前,所以需要逆序)
    20         printLCS(str1,str2,flag,i-1,j-1,str);
    21     }
    22     else if("left"==flag[i][j])
    23         printLCS(str1,str2,flag,i-1,j,str);
    24     else if("up"==flag[i][j])
    25         printLCS(str1,str2,flag,i,j-1,str);
    26 }
    27 
    28 int findLCS(const string& str1,const string& str2) {
    29     // write code here
    30     if(0 == str1.size() || 0 == str2.size())
    31         return 0;
    32     vector<vector<string> > flag(str1.size()+1,vector<string>(str2.size()+1,""));//记录状态
    33     vector<vector<int> > array(str1.size()+1,vector<int>(str2.size()+1,0));
    34     for(size_t i=1; i <= str1.size(); ++i){//注意:是小于等于
    35         for(size_t j=1; j<= str2.size(); ++j){//注意:是小于等于
    36             if(str1[i-1] == str2[j-1]){//前面填充了一行一列,因此判断i-1和j-1
    37                 array[i][j] = array[i-1][j-1]+1;
    38                 flag[i][j] = "left_up";
    39             }
    40             else
    41             {
    42                 if(array[i-1][j] >= array[i][j-1])
    43                 {
    44                     array[i][j] = array[i-1][j];
    45                     flag[i][j] = "left";
    46                 }
    47                 else/*(array[i-1][j] < array[i][j-1])*/
    48                 {
    49                     array[i][j] = array[i][j-1];
    50                     flag[i][j] = "up";
    51                 }
    52             }
    53         }
    54     }
    55 
    56     string str="";
    57     printLCS(str1,str2,flag,str1.size(),str2.size(),str);
    58     cout<<"公共子序列: "<<str<<endl;
    59     return array[str1.size()][str2.size()];
    60 }
    61 
    62 int main(){
    63     std::string str1,str2;
    64     while(getline(cin,str1),getline(cin,str2)){
    65         cout<<findLCS(str1,str2)<<endl;
    66     }
    67     return 0;
    68 }

     最长公共子序列扩展题(注意思维的转换):

     1 /*
     2 本程序说明:
     3 
     4 给定一个数组,插入元素使得它成为回文串,要求所得回文串所有数字之和最小。
     5 
     6 */
     7 #include <iostream>
     8 #include <vector>
     9 #include <algorithm>
    10 using namespace std;
    11 
    12 int main()
    13 {
    14     int n;
    15     while(cin>>n){
    16         vector<int> v(n);
    17         int sum=0;
    18         for(int i=0;i<n;++i){
    19             cin>>v[i];
    20             sum+=v[i];
    21         }
    22 
    23         vector<int> rv=v;
    24         reverse(rv.begin(),rv.end());
    25 
    26         //这段程序其实就是原数组和逆序数组求公共子序列,得到最大子序列的和,
    27         //剩下要插入的数字之和就是原数组的和减去公共子序列的和
    28         vector<vector<int>> dp(n+1,vector<int>(n+1,0));
    29         for(int i=1;i<=n;++i){
    30             for(int j=1;j<=n;++j){
    31                 if(v[i-1]==rv[j-1]){
    32                     dp[i][j]=dp[i-1][j-1]+v[i-1];
    33                 }
    34                 else{
    35                     dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
    36                 }
    37             }
    38         }
    39 
    40 //        for(int i=0;i<n+1;++i){
    41 //            for(int j=0;j<n+1;++j){
    42 //                cout<<dp[i][j]<<" ";
    43 //            }
    44 //            cout<<endl;
    45 //        }
    46 
    47         cout<<sum+(sum-dp[n][n])<<endl;//其中sum-dp[n][n]是需要插入的数字和
    48     }
    49     return 0;
    50 }

    参考文章:http://www.cnblogs.com/huangxincheng/archive/2012/11/11/2764625.html

    『注:本文来自博客园“小溪的博客”,若非声明均为原创内容,请勿用于商业用途,转载请注明出处http://www.cnblogs.com/xiaoxi666/』
  • 相关阅读:
    Android Studio 中关于NDK编译及jni header生成的问题
    为YAESU FT-817ND 增加频谱功能
    Nagios 安装配置
    ubuntu 13.10 Ralink RT3290 无线与蓝牙4.0的驱动安装
    golang全文搜索--使用sphinx
    GNU Radio 之 rtl-sdr
    OsmocomBB && Motorora C118
    OsmocomBB 编译安装
    Go 若干技巧
    Docker内核知识
  • 原文地址:https://www.cnblogs.com/xiaoxi666/p/7339878.html
Copyright © 2011-2022 走看看