1. 程式人生 > >【動態規劃】最長公共子序列問題

【動態規劃】最長公共子序列問題

clas == 搜索 ios for 參考 pan 公式 是否

題目描述:

給定兩個字符串s1s2……sn和t1t2……tn。求出這兩個字符串最長的公共子序列的長度。字符串s1s2……sn的子序列指可以表示為si1si2……sim(i1<i2<……<im)的序列。(n>=1,m<=1000)

輸入:

n=4

m=4

s="abcd"

t="becd"

輸出:

3

分析:

階段就是n=0,1,2,3,4……時,m=0,1,2,3……1000時;狀態就是對應字符相等還是不相等。

看該題是否符合,每個階段的最優狀態可以從之前的某個階段的某個或某些狀態得到而不管之前的狀態是如何得到的(最優子結構和無後效性)。

用輸入輸出的栗子證一下:

當n=4,m=4時,S3為‘d’,T3為‘d’,S3==T3,則此階段對應的最長公共子序列(LCS)為n=3,m=3時的LCS加1。

當n=2,m=2時,S1為‘b‘,T1為‘e‘,S1!=T3,則此階段對應的LCS為,n=2,m=1階段的LCS與n=1,m=2階段的LCS的最大值。

記憶化搜索代碼:

#include <iostream>
#include <string>
#include <string.h>
#define MAX_N 1001
#define MAX_M 1001
using namespace std;
int n,m;
string s,t;
int dp[MAX_N][MAX_M];
int rec(int ni,int mi){
if(dp[ni][mi]>=0
){ return dp[ni][mi]; } int res; //當s和t的字符串長度為1時,它們參考的前一個LCS設為0 if(ni==0||mi==0){ res=0; } else if((ni>0&&ni<=n)&&(mi>0&&mi<=m)){ if(s[ni-1]==t[mi-1]){ res=rec(ni-1,mi-1)+1; } else if(s[ni-1]!=t[mi-1]){ res=max(rec(ni-1,mi),rec(ni,mi-1)); } } dp[ni][mi]
=res; return res; } int main() { cin>>n>>m; cin>>s>>t; memset(dp,-1,sizeof(dp)); cout<<rec(n,m)<<endl; for(int i=0;i<=n;i++){ for(int j=0;j<=m;j++){ cout<<dp[i][j]; if(j<m){ cout<< ; } else if(j==m){ cout<<endl; } } } return 0; }

根據記憶化搜索推到出遞推公式,本題比較簡單也可以直接寫出來:

當m或n等於0,dp[n][0]=0,dp[0][m]=0;

當Si與Ti相等時,dp[n][m]=dp[n-1][m-1]+1;

當Si與Ti不相等時,dp[n][m]=max(dp[n-1][m],dp[n][m-1]);

舉個不相等的栗子:

"abcdef"和"defihg",‘f‘和‘g‘不相等,則考慮"abcdef"和"defih"其LCS為3。

動態規劃的代碼:

#include <iostream>
#include <string>
#define MAX_N 1001
#define MAX_M 1001
using namespace std;
int dp[MAX_N][MAX_M];
int main()
{
    int n,m;
    string s,t;

    cin>>n>>m;
    cin>>s>>t;
    //遞推公式中的n用i+1表示,m用j+1表示
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(s[i]==t[j]){
                dp[i+1][j+1]=dp[i][j]+1;
            }
            else if(s[i]!=t[j]){
                dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j]);
            }
        }
    }
    cout<<dp[n][m]<<endl;
    return 0;
}

【動態規劃】最長公共子序列問題