最長共通部分列(DP、動的計画法)

最長共通部分列

タイトルの説明
シーケンスXと別のシーケンスZを指定します。Zのすべての要素がXに存在し、Xの添え字の順序が厳密に増加している場合、ZはXのサブシーケンスと呼ばれます。
例:Z = <a、b、f、c>はシーケンスX = <a、b、c、f、b、c>のサブシーケンスであり、XのZの要素のサブスクリプトシーケンスは<1、2です。 4,6>。
ここで、2つのシーケンスXとYを指定します。それらの最長共通部分列の長さはどれくらいですか?

入力
入力には、複数のテストデータセットが含まれています。入力の各グループは1行を占め、いくつかのスペースで区切られた2つの文字列です。各文字列の長さは100を超えません。

出力
入力の各セットについて、2つの文字列の最長共通部分列の長さを出力します。

サンプル入力
abcfbcabfcab
プログラミングコンテスト
abcdmnp

出力例
4
2
0

サブシーケンス:元のシーケンスの順序に関連する元のシーケンスのサブセット

例:ABCSCYのサブシーケンスBSY

共通のサブシーケンスは、2つのシーケンスで共有されるサブシーケンスです。たとえば、ABCSCYとABFSGPにはBSYシーケンスがあります。

したがって、最長共通部分列は、両端の文字列の最長共通部分列です。

問題解決

1.暴力法

これには、文字列の長さに指数関数的に関連する順列と組み合わせが含まれます

2.動的計画法

2.1サブシーケンスを見つけ、2つのサブシーケンスの最後のビットを検討します

A = a1、a2、a3、a4 ... ax B = b1、b2、b3、...最長共通部分列Lcs(x、y)= Z = z1、z2 ... zm;の長さを見つけると仮定します。 Zのはlcs(x、y)として計算されます

(1)ax = by

AxとByの最後の桁ax = by = tとすると、Zの最後の桁Lcs(x、y)= zm = t

証明:(矛盾により)Lcs(x、y)の最後の桁はAa = Bbはtではなく、a <x、b <yの場合、共通部分列の後ろにtを追加して、より長いシーケンスを取得できます。 、および仮説の矛盾

したがって、zm = t

zm = tの場合、Ax = Byのとき、lcs(x、y)= lcs(x-1、y-1)+1を取得できます。

(2)Ax≠By

ここで、Lcs(x、y)の最後の桁をtとします。

(2.1)t≠ax、有lcs(x、y)= lcs(x-1、y)

(2.2)t≠by、有lcs(x、y)= lcs(x、y-1)

両方のケースが発生する可能性があるため、最長共通部分列の定義によれば、lcs(x、y)= max(lcs(x-1、y)、lcs(x、y-1))

(3)AxまたはByが空のシーケンスの場合、lcs(x、y)= 0

したがって、次の漸化式が得られます

lcs(x、y)=

(1)lcsx-1、y-1)+1(ax = by = t)

(2)max(lcs(x-1、y)、lcs(x、y-1))(t≠ax、t≠by)

(3)0AxeまたはByが空です

漸化式を見つけた後、次の2つのアイデアがあります。①漸化式を使用して後ろから前に再帰する②漸化式の重複が多すぎるため、動的計画法を前から後ろに使用する例:lcs(x-1、 y)にはlcs(x-1、y-1)が含まれます。解くと、複数回解かれ、時間の複雑さが増すため、ここでは前から後ろに動的計画法を使用することを選択します。プッシュ、サブの解を保存します。 -将来使用するための配列の問題。これにより、時間の複雑さが軽減されます。

上記の漸化式から、2次元配列c [m] [n]を使用してサブ問題の結果を格納することは簡単に考えられます。

以下は、配列の再帰式です。

この2次元配列から、最長共通部分列の長さを簡単に取得できますが、最長共通部分列自体を取得するにはどうすればよいでしょうか。

下の写真を例にとると、2次元配列に戻ると、2次元配列の解法プロセスを簡単に確認できます。

ax = by = tが発生すると、図の斜めの矢印になり、レコードが発生する位置に文字が出力されます。配列をトラバースすると、最長共通部分列も出力されます。

ここに画像の説明を挿入

#include<stdio.h>
#include<string.h>
int f(int a,int b)
{
    
    
    if(a>=b)
    return a;
    else
    return b;
}
int main()
{
    
    
    char a[101],b[101];
    int i,x,y,j,k,n,m,l;
    while(scanf("%s%s",a,b)!=EOF)
    {
    
    
        int t=0;int d[101][101];
        x=strlen(a),y=strlen(b);
        for(i=0;i<x;i++)
        for(j=0;j<y;j++)
        d[i][j]=0;
        for(i=1;i<=x;i++)
        {
    
    
            for(j=1;j<=y;j++)
          {
    
    
               
              if(a[i-1]==b[j-1])
               d[i][j]=d[i-1][j-1]+1;
               else
               d[i][j]=f(d[i-1][j],d[i][j-1]);//这里可以用C++的max函数
          } 
         
        }
        /*for(i=1;i<=x;i++)
        {
           for(j=1;j<=y;j++)
           printf("%d ",d[i][j]);
            printf("\n");
        }*/
        printf("%d\n",d[x][y]);
    }
    return 0;
}

おすすめ

転載: blog.csdn.net/m0_46381590/article/details/111417511