F - LCS
キーワード
解説
と を左から見ていき、一致する文字を貪欲に取ることで、最長共通部分列を求めることができる。
として、 に対して、 を次のように定義する。
の最初の 文字と の最初の 文字の最長共通部分列の長さ
の最初の 文字と の最初の 文字の最長共通部分列の長さは、
- の 文字目と の 文字目が一致する場合、 の最初の 文字と の最初の 文字の最長共通部分列の長さに を足したもの
- そうでない場合、 の最初の 文字と の最初の 文字の最長共通部分列の長さと の最初の 文字と の最初の 文字の最長共通部分列の長さの大きい方
となる。よって、次の漸化式が成り立つ。
計算量は一回の遷移に かかるので、全体で となる。
最終的な答えは となる。
復元
最長共通部分列を復元するためには、 から逆順に遡っていく。
- が と等しいまたは と等しい場合、 は または から遷移してきたことになる。この場合、 と は共通部分列に含まれない。
- そうでない場合、 を共通部分列の先頭に追加し、 に遷移する。
これを に到達するまで繰り返すことで、最長共通部分列を復元できる。
提出コード
int main() {
string s, t; cin >> s >> t;
vector<vector<int>> dp(s.size() + 1, vector<int>(t.size() + 1, 0));
rep(i,1,s.size()+1) {
rep(j,1,t.size()+1) {
if (s[i-1] == t[j-1]) dp[i][j] = dp[i-1][j-1] + 1;
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
int i = s.size(), j = t.size();
string ans = "";
while (i > 0 && j > 0) {
if (s[i-1] == t[j-1]) {
ans = s[i-1] + ans;
i--; j--;
} else if (dp[i-1][j] > dp[i][j-1]) i--;
else j--;
}
cout << ans << endl;
}