Atcoder DP Contest pF
題敘
https://atcoder.jp/contests/dp/tasks/dp_f
給兩個字串,問兩字串的LCS,並輸出最大的LCS
想法
把兩個字串拆成:
s = sub1 + e1
t = sub2 + e2
另$LCS(i,j)$ 表示 $s[0i]$ 和 $t[0j]$ 的LCS長度
分成四種情況討論
- LCS包含 $e1$ ,不含 $e2$
$LCS(s,t) = LCS(s,sub2)$
- LCS包含 $e1$ ,包含 $e2$
$LCS(s,t) = LCS(sub1,sub2)+1$
- LCS不含 $e1$ ,不含 $e2$
$LCS(s,t) = LCS(sub1,sub2)$
- LCS不含 $e1$ ,包含 $e2$
$LCS(s,t) = LCS(sub1,t)$
總的來說,定義 $DP[i][j]$ 表示$LCS(i,j)$的長度
則有轉移式
$DP[i][j] = DP[i-1][j-1]+1\ ,s[i]=t[j]$
$DP[i][j] = max(DP[i-1][j], DP[i],[j-1])\ ,s[i] \neq t[j]$
且已知
$DP[i][0] = 0, DP[0][j] = 0$
這邊能獲得的僅僅是LCS長度,但題目所求的是字串
可以另外開一個陣列紀錄他是從哪裡轉移過來,最後遞迴輸出結果即可
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| #include<bits/stdc++.h> #define LL long long #define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); #define pii pair<int,int> using namespace std; string s,t; int dp[3005][3005],from[3005][3005];
void print_LCS(int i,int j){ if(i==0 || j==0) return; if(from[i][j] == 0){ print_LCS(i-1,j-1); cout<<s[i-1]; } else if(from[i][j] == 1) print_LCS(i-1,j); else print_LCS(i,j-1); }
int main(){ IOS while(cin>>s>>t){ for(int i=0 ; i<3005 ; i++){ dp[i][0] = dp[0][i] = 0; } for(int i=1 ; i<=s.size() ; i++){ for(int j=1 ; j<=t.size() ; j++){ if(s[i-1] == t[j-1]){ dp[i][j] = dp[i-1][j-1]+1; from[i][j]=0; } else{ if(dp[i-1][j] > dp[i][j-1]) from[i][j]=1; else from[i][j]=2; dp[i][j] = max(dp[i-1][j], dp[i][j-1]); } } } print_LCS(s.size(),t.size()); } return 0; }
|
複雜度
有 $len(s)*len(t)$ 種狀態,每種狀態轉移時間複雜度為 $O(1)$
DP複雜度為 $O(len(s)*len(t))$
輸出複雜度大約為 $O(len(s)+len(t))$
總複雜度 $O(len(s)*len(t))$