#include <bits/stdc++.h>
using namespace std;
#pragma GCC optimize ("O3") // Optimization directive for GCC
#define ios cin.tie(0), ios::sync_with_stdio(false) // Speeds up I/O
// Global declaration of dp and prestate. This is generally not recommended for large
// arrays due to potential stack overflow, but for competitive programming with
// limits like 3005, it might be intended for static allocation in the data segment.
// However, 'n' and 'm' are not defined at this scope, so this will cause a compilation error.
// These should be declared inside main after reading n and m.
// vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0)); // Error: n and m not defined
// pair<int, int> prestate[3005][3005]; // This is okay as a global with fixed size
signed main(){
ios;
string s, s2;
cin >> s >> s2;
int n = s.size();
int m = s2.size();
// Declare dp and prestate here after n and m are known.
vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
pair<int, int> prestate[3005][3005]; // Assuming max size 3000 for strings
s = " " + s;
s2 = " " + s2;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if(s[i] == s2[j]){
dp[i][j] = dp[i - 1][j - 1] + 1;
prestate[i][j] = {i - 1, j - 1};
}else{
if(dp[i][j - 1] > dp[i - 1][j]){
dp[i][j] = dp[i][j - 1];
prestate[i][j] = {i, j - 1};
}else{
dp[i][j] = dp[i - 1][j];
prestate[i][j] = {i - 1, j};
}
}
}
}
string ans = "";
// Reconstruction loop.
// The condition `while(n != 0 || m != 0)` is mostly correct for tracing back
// to the base case (0, 0).
// However, the logic inside has issues.
int cur_n = n; // Use different variable names to avoid shadowing
int cur_m = m;
while(cur_n > 0 || cur_m > 0){ // Continue as long as we are not at (0,0)
// The condition `if(s[n] == s[m])` is incorrect. It should compare
// characters from the original strings `s` and `s2` at the current indices `cur_n` and `cur_m`.
// Also, the character should only be added to the answer when the move was a diagonal one (indicating a match).
if(prestate[cur_n][cur_m].first == cur_n - 1 && prestate[cur_n][cur_m].second == cur_m - 1){
// This indicates a diagonal move, meaning s[cur_n] == s2[cur_m] was true in the DP step.
ans += s[cur_n]; // Append the character that was part of the LCS
// cout << s[cur_n]; // Printing here will print in reverse order
}
// Move to the previous state based on prestate
int prevn = prestate[cur_n][cur_m].first;
int prevm = prestate[cur_n][cur_m].second;
cur_n = prevn; // Update current indices
cur_m = prevm;
// The lines `int n = prevn; int m = prevm;` inside the loop
// declare new local variables `n` and `m` that shadow the outer `n` and `m`.
// This is incorrect and the loop condition should use the updated `cur_n` and `cur_m`.
}
// The LCS is built in reverse order, so reverse it.
reverse(ans.begin(), ans.end());
cout << ans << endl;
return 0;
}