Problem: 2050E - Three Strings
Language: C++23 (GCC 14-64, msys2)

Previous submission (verdict: Time limit exceeded on test 16):
// Every problem you solve is a step closer to mastering
// the art of thinking. Embrace the challenge,
// because every failure is just a lesson on your way to victory

#include <bits/stdc++.h>
using namespace std;

// #include <ext/pb_ds/hash_policy.hpp> // For hash policy
#include <cmath> // For acos

using namespace std;
// using namespace __gnu_pbds; // For policy-based data structures
using ll = long long; // Type alias for long long
#define int long long
#define db double
#define MOP 998244353
#define N 2e5 + 5
#define fr(i, a, b) for (int i = a; i >= b; i--)
#define lcm(a, b) ((a) * (b)) / __gcd(a, b)
#define inp(a, n)               \
    for (int r = 0; r < n; r++) \
    cin >> a[r]
#define out(a, n)               \
    for (int v = 0; v < n; v++) \
    cout << a[v] << " "
#define f(i, a, b) for (ll i = a; i <= b; i++)
#define u(a, b) for (int i = 0; i < a; i++)
#define s(v) sort(v.begin(), v.end())
#define sr(v) sort(v.end(), v.begin())
#define MOD 1000000007
#define ci(n) cin >> n
#define co(n) cout << n << " "
#define si(v) v.size()
#define py cout << "YES" << " "
#define pn cout << "NO" << " "
#define F first
#define ld long double
#define S second
#define pb(v, x) v.push_back(x)
#define pob(v) v.pop_back()
#define rev(v) reverse(v.begin(), v.end())
#define revn(v) reverse(v.end(), v.begin())
#define d_r(v) v.erase(unique(v.begin(), v.end()), v.end());
#define en cout << endl
#define all(v) v.begin(), v.end()
#define len(str) str.length()
#define each(it, m) for (auto &it : m)
#define y_n(ans)       \
    if (ans)           \
    {                  \
        cout << "YES"; \
    }                  \
    else               \
    {                  \
        cout << "NO";  \
    }
#include <functional>
#include <utility>
#ifdef DEBUG
#define dbg(...) __VA_ARGS__
#else
#define dbg(...)
#endif
#define llmin LLONG_MIN
#define llmax LLONG_MAX
template <typename F>
struct y_combinator_result
{
    F fi;
    template <typename... Args>
    decltype(auto) operator()(Args &&...args) const
    {
        return fi(*this, std::forward<Args>(args)...);
    }
};
template <typename F>
decltype(auto) y_combinator(F &&fi)
{
    return y_combinator_result<std::decay_t<F>>{std::forward<F>(fi)};
}
template <typename T>
using V = vector<T>;
template <typename T1, typename T2>
using P = pair<T1, T2>;
template <typename T>
using pqmax = priority_queue<T>;
template <typename T>
using pqmin = priority_queue<T, vector<T>, greater<T>>;
template <typename K, typename V>
using UMP = unordered_map<K, V>;
template <typename K, typename V>
using MP = map<K, V>;
template <typename T>
using SE = set<T>;
template <typename T>
using USE = unordered_set<T>;
struct pair_hash
{
    template <class T1, class T2>
    std::size_t operator()(const std::pair<T1, T2> &pair) const
    {
        return std::hash<T1>()(pair.first) ^ std::hash<T2>()(pair.second);
    }
};
ll add(ll a, ll b, ll MO) { return ((a % MO + b % MO) % MO); }
long long factorialDP(ll n)
{
    vector<long long> dp(n + 1, 1);
    for (ll i = 2; i <= n; ++i)
    {
        dp[i] = (1LL * dp[i - 1] * i) % MOD;
    }
    return (dp[n]);
}
bool binary(vector<int> &v, int i, int j, int k)
{
    if (i >= j)
    {
        return false;
    }
    int mid = (i + j) / 2;
    bool a = false, b = false;
    if (v[i] == k || v[j] == k || v[mid] == k)
    {
        return true;
    }
    if (v[mid] < k)
    {
        a = binary(v, mid + 1, j, k);
    }
    if (v[mid] > k)
    {
        b = binary(v, i, mid, k);
    }
    return a || b;
}
int ternarySearch(vector<int> &v, int left, int right, int key) { return (right >= left) ? ((v[left + (right - left) / 3] == key) ? left + (right - left) / 3 : ((v[right - (right - left) / 3] == key) ? right - (right - left) / 3 : ((key < v[left + (right - left) / 3]) ? ternarySearch(v, left, left + (right - left) / 3 - 1, key) : ((key > v[right - (right - left) / 3]) ? ternarySearch(v, right - (right - left) / 3 + 1, right, key) : ternarySearch(v, left + (right - left) / 3 + 1, right - (right - left) / 3 - 1, key))))) : -1; }
bool palindrome(vector<int> &v)
{
    return (v.size() == 1) ? true : [&]()
    { int x = v.size() / 2; for (int t = 0; t < x; t++) { if (v[t] != v[v.size() - t - 1]) return false; } return true; }();
}
int xor_from_1(int n) { return ((n % 4 == 0) ? n : (n % 4 == 1) ? 1
                                               : (n % 4 == 2)   ? n + 1
                                                                : 0); }
void subsets(vector<ll> &v, int n)
{
    for (int i = 0; i < (1 << n); i++)
    {
        vector<ll> ans;
        for (int j = 0; j < n; j++)
            if (i & (1 << j))
                ans.push_back(v[j]);
        for (auto it : ans)
            cout << it << " ";
        cout << endl;
    }
}
string binary_rep(int n)
{
    if (n == 0)
    {
        return "0";
    }
    bitset<128> binary(n);
    string ans = "";
    for (size_t g = 0; g < 64; g++)
        ans += binary[g] + '0';
    rev(ans);
    size_t it = ans.find_first_not_of('0');
    return ans.substr(it);
}
int search(vector<ll> &v, ll target)
{
    int low = 0, high = v.size() - 1;
    while (low <= high)
    {
        int mid = low + (high - low) / 2;
        if (v[mid] < target && v[mid + 1] > target)
            return v[mid];
        if (v[mid] < target)
            low = mid + 1;
        else
            high = mid - 1;
    }
    return high;
}
ll max_len_string(string str)
{
    ll len = 1, ans = 1;
    ll n = str.length();
    f(i, 1, n - 1)
    {
        if (str[i] >= str[i - 1])
            len++;
        else
        {
            ans = max(ans, len);
            len = 1;
        }
        ans = max(ans, len);
    }
    return ans;
}
void dfs(string str, vector<int> &dp, unordered_map<int, list<int>> &m, int n, unordered_map<int, bool> &visited, int node)
{
    visited[node] = 1;
    if (si(m[node]) == 0)
    {
        dp[node] = ((str[node - 1] == 'W') ? 1 : -1);
        return;
    }
    for (auto it : m[node])
    {
        if (!visited[it])
        {
            dfs(str, dp, m, n, visited, it);
            dp[node] += dp[it];
        }
    }
    dp[node] += ((str[node - 1] == 'W') ? 1 : -1);
}
ll nextint(ll a, ll b)
{
    ll x = ceil(((double)a) / (double)b);
    return x;
}
void sieveOfEratosthenes(int n, vector<bool> &prime)
{
    prime[0] = prime[1] = false;
    for (int p = 2; p * p <= n; p++)
        if (prime[p])
            for (int i = p * p; i <= n; i += p)
                prime[i] = false;
}
long long largestPrimeFactor(long long n)
{
    long long largest = -1;
    while (n % 2 == 0)
    {
        largest = 2;
        n /= 2;
    }
    for (long long i = 3; i <= sqrt(n); i += 2)
    {
        while (n % i == 0)
        {
            largest = i;
            n /= i;
        }
    }
    if (n > 2)
    {
        largest = n;
    }
    return largest;
}
bool isPalindrome(const std::string &str) { return std::equal(str.begin(), str.begin() + str.size() / 2, str.rbegin()); }
// #pragma once
// struct chash { // large odd number for C
// 	const uint64_t C = (long long)(4e18 * acos(0)) | 71;
// 	ll operator()(ll x) const { return __builtin_bswap64(x*C); }
// };
// __gnu_pbds::gp_hash_table<ll,int,chash> h({},{},{},{},{1<<16});
struct custom_hash
{
    static uint64_t splitmix64(uint64_t x)
    {
        // http://xorshift.di.unimi.it/splitmix64.c
        x += 0x9e3779b97f4a7c15;
        x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
        x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
        return x ^ (x >> 31);
    }

    size_t operator()(uint64_t x) const
    {
        static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count();
        return splitmix64(x + FIXED_RANDOM);
    }
    template <class T1, class T2>
    size_t operator()(const pair<T1, T2> &p) const
    {
        auto h1 = (*this)(static_cast<uint64_t>(p.first));
        auto h2 = (*this)(static_cast<uint64_t>(p.second));
        return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2));
    }
    template <class T>
    size_t operator()(const vector<T> &v) const
    {
        size_t seed = v.size();
        for (auto &i : v)
        {
            seed ^= (*this)(static_cast<uint64_t>(i)) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
        }
        return seed;
    }
};
long long countseti(long long n, int i)
{
    long long power = 1LL << i;
    long long complete_blocks = (n + 1) / (2 * power);
    long long remainder = (n + 1) % (2 * power);
    long long count_set = complete_blocks * power + max(0LL, remainder - power);
    return count_set;
}
ll getor(const ll L, const ll R)
{
    ll ans = 0;
    y_combinator([&](const auto &self, const ll l, const ll r) -> void
                 {
        if(r<L||R<l)
            return;
        if(L<=l&&r<=R){
            ans|=r;
            return;
        }
        const ll m=(l+r)/2;
        self(l,m);
        self(m+1,r); })(0, (1LL << 60) - 1);
    return ans;
}
struct hash_pair
{
    template <class T1, class T2>
    size_t operator()(const pair<T1, T2> &p) const
    {
        auto hash1 = hash<T1>{}(p.first);
        auto hash2 = hash<T2>{}(p.second);
        return hash1 ^ hash2;
    }
};
bool prime(int n)
{
    if (n <= 1)
        return false;
    if (n <= 3)
        return true;
    if (n % 2 == 0 || n % 3 == 0)
        return false;
    for (int i = 5; i <= sqrt(n); i += 6)
    {
        if (n % i == 0 || n % (i + 2) == 0)
            return false;
    }
    return true;
}
int VAL;
int arr[(unsigned long)N];
struct Query
{
    int l, r;
    int id;
    bool operator<(const Query &other)
    {
        if (l / VAL == other.l / VAL)
            return r < other.r;
        return l / VAL < other.l / VAL;
    }
};
vector<Query> queries;
class Mo_Algorithm
{
private:
    vector<ll> ans;   // answer for each queries
    vector<int> freq; // frequency of values
    int cur_l, cur_r; // current left and right
    ll cur_res;       // current result

    Mo_Algorithm() = default;

    void add(int x)
    {
        /*
        Every time we add x
        freq[x] * freq[x] * x will become (freq[x] + 1) * (freq[x] + 1) * x
        Let's subtract the latters by the formers, we have 2 * freq[x] * x + x

        similar for remove function
        */
        cur_res += 2ll * freq[x] * x + x;
        freq[x]++;
    }

    void remove(int x)
    {
        cur_res -= 2ll * freq[x] * x - x;
        freq[x]--;
    }

    void convert(int l, int r, int id, int *arr)
    {
        // shifting cur_l and cur_r to the right position cur_l -> l, cur_r -> r
        while (cur_l < l)
        {
            remove(arr[cur_l]);
            cur_l++;
        }
        while (cur_r < r)
        {
            cur_r++;
            add(arr[cur_r]);
        }
        while (cur_l > l)
        {
            cur_l--;
            add(arr[cur_l]);
        }
        while (cur_r > r)
        {
            remove(arr[cur_r]);
            cur_r--;
        }

        // update result
        ans[id] = cur_res;
    }

public:
    Mo_Algorithm(int l, int r) : cur_l(l), cur_r(r)
    {
        cur_res = 0;
        ans.resize(2e5, 0ll);
        freq.resize(1e6 + 5, 0);
    }

    void process(Query q, int *arr)
    {
        convert(q.l, q.r, q.id, arr);
    }

    void print(int n)
    {
        for (int i = 0; i < n; i++)
            cout << ans[i] << '\n';
    }
};

int KMP(const string &pattern, const string &text)
{
    int m = len(pattern);
    int j = 0;
    V<int> prefix(m, 0);
    f(i, 1, m - 1)
    {
        while (j > 0 && pattern[j] != pattern[i])
        {
            j = prefix[j - 1];
        }
        if (pattern[i] == pattern[j])
        {
            j++;
        }
        prefix[i] = j;
    }
    int n = len(text);
    j = 0;
    for (int i = 0; i < n; i++)
    {
        while (j > 0 && text[i] != pattern[j])
        {
            j = prefix[j - 1];
        }
        if (text[i] == pattern[j])
        {
            j++;
        }
        if (j == m)
        {
            // cout << "Pattern found at index " << i - m + 1 << endl;
            j = prefix[j - 1];
            return 1;
        }
    }
    return 0;
}
int nCr(int n, int m)
{
    int a = factorialDP(n);
    int b = factorialDP(n - m);
    int c = factorialDP(m);
    int k = b * c;
    int ans = a / k;
    return ans;
}
class SegmentTree
{
private:
    vector<int> tree;
    int n;
    void build(int node, int start, int end, int value, V<int> &v)
    {
        if (start == end)
        {
            tree[node] = v[start];
        }
        else
        {
            int mid = (start + end) / 2;
            build(2 * node, start, mid, 0, v);
            build(2 * node + 1, mid + 1, end, 0, v);
            tree[node] = max(tree[2 * node], tree[2 * node + 1]);
        }
    }
    void update(int node, int start, int end, int idx, int value, V<int> &v)
    {
        if (start == end)
        {
            tree[node]++;
        }
        else
        {
            int mid = (start + end) / 2;
            if (start <= idx && idx <= mid)
            {
                update(2 * node, start, mid, idx, value, v);
            }
            else
            {
                update(2 * node + 1, mid + 1, end, idx, value, v);
            }
            tree[node] = max(tree[2 * node], tree[2 * node + 1]);
        }
    }
    int query(int node, int start, int end, int L, int R)
    {
        if (R < start || end < L)
        {
            return INT_MIN;
        }
        if (L <= start && end <= R)
        {
            return tree[node];
        }
        int mid = (start + end) / 2;
        int leftQuery = query(2 * node, start, mid, L, R);
        int rightQuery = query(2 * node + 1, mid + 1, end, L, R);
        return max(leftQuery, rightQuery);
    }

public:
    SegmentTree(vector<int> &arr)
    {
        n = arr.size();
        tree.resize(4 * n);
        build(1, 0, n - 1, 0, arr);
    }
    void update(int l, int r)
    {
        update(l, r);
    }
    int queryma(int l, int r)
    {
        return query(1, 0, n - 1, l, r);
    }
};
class DSU
{
    vector<int> parent, rank, size;

public:
    DSU(int n) : parent(n + 1), rank(n + 1, 0), size(n + 1, 1)
    {
        for (int i = 0; i <= n; ++i)
        {
            parent[i] = i;
        }
    }
    int find(int node)
    {
        if (node != parent[node])
        {
            parent[node] = find(parent[node]);
        }
        return parent[node];
    }

    void unionByRank(int u, int v)
    {
        int rootU = find(u);
        int rootV = find(v);

        if (rootU != rootV)
        {
            if (rank[rootU] < rank[rootV])
            {
                parent[rootU] = rootV;
            }
            else if (rank[rootU] > rank[rootV])
            {
                parent[rootV] = rootU;
            }
            else
            {
                parent[rootU] = rootV;
                rank[rootV]++;
            }
        }
    }
    void unionBySize(int u, int v)
    {
        int rootU = find(u);
        int rootV = find(v);

        if (rootU != rootV)
        {
            if (size[rootU] < size[rootV])
            {
                parent[rootU] = rootV;
                size[rootV] += size[rootU];
            }
            else
            {
                parent[rootV] = rootU;
                size[rootU] += size[rootV];
            }
        }
    }
    int maxi()
    {
        int k = *max_element(all(size));
        return k;
    }
};
int mul(int a, int b, int mod_value)
{
    return (a * 1LL * b) % mod_value;
}
int gcd(int a, int b)
{
    while (b != 0)
    {
        int temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}
int lcmx(int a, int b)
{
    return (a / gcd(a, b)) * b;
}
long long modExp(long long base, long long exp, long long mod)
{
    long long result = 1;
    while (exp > 0)
    {
        if (exp % 2 == 1)
        {
            result = (result * base) % mod;
        }
        base = (base * base) % mod;
        exp /= 2;
    }
    return result;
}
struct cmp
{
    bool operator()(const pair<int, int> &a, const pair<int, int> &b)
    {
        return a.first * b.second > a.second * b.first;
    }
};
bool isPerfectSquare(int n)
{
    int root = sqrt(n);
    return root * root == n;
}
int modularDivision(int x, int a, int m)
{
    int a_inv = modExp(a, m - 2, m);
    return (x * a_inv) % m;
}
struct cmpx
{
    bool operator()(const pair<int, int> &a, const pair<int, int> &b) const
    {
        if (a.first == b.first)
        {
            return a.second > b.second;
        }
        return a.first > b.first;
    }
};
bool cmpx1(const pair<int, int> &a, const pair<int, int> &b)
{
    if (a.first == b.first)
    {
        return a.second > b.second;
    }
    return a.first > b.first;
}
int ans(string a,string b,string c,int s1,int s2,int s3,vector<vector<int>>&dp){
    if(s1==si(a)){
        int ans=0;
        f(j,s3,len(c)-1){
            if(b[s2]!=c[j]){
                ans++;
            }
            s2++;
        }
        return ans;
    }
    if(s2==si(b)){
        int ans=0;
        f(j,s3,len(c)-1){
            if(a[s1]!=c[j]){
                ans++;
            }
            s1++;
        }
        return ans;
    }
    if(dp[s1][s2]!=-1){
        return dp[s1][s2];
    }
    int movefroma=ans(a,b,c,s1+1,s2,s3+1,dp);
    if(a[s1]!=c[s3]){
        movefroma++;
    }
    int movefromb=ans(a,b,c,s1,s2+1,s3+1,dp);
    if(b[s2]!=c[s3]){
        movefromb++;
    }
    return dp[s1][s2]=min(movefroma,movefromb);
}
void solve()
{
   string a,b,c;ci(a);ci(b);ci(c);int n;
   vector<vector<int>>dp(si(a)+1,vector<int>(si(b)+1,-1));
   co(ans(a,b,c,0,0,0,dp));
}
int32_t main()
{
    int t;
    t = 1;
    cin >> t;
    f(i, 1, t)
    {
        solve();
        en;
    }
    // #ifndef ONLINE_JUDGE
    // cerr << "Time : " << 1000 * ((double)clock()) / (double)CLOCKS_PER_SEC << "ms\n";
    // #endif
}

Submission (verdict: Accepted):
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

int findMinChanges(string& a, string& b, string& c) {
    int len_a = a.size(), len_b = b.size(), len_c = c.size();
    if (len_a + len_b != len_c) return -1;
    vector<vector<int>> dp(len_a + 1, vector<int>(len_b + 1, INT_MAX));
    dp[0][0] = 0;
    for (int s1 = 0; s1 <= len_a; ++s1) {
        for (int s2 = 0; s2 <= len_b; ++s2) {
            int s3 = s1 + s2;
            if (s3 > len_c) break;
            if (s1 < len_a && s3 < len_c) {
                dp[s1 + 1][s2] = min(dp[s1 + 1][s2], dp[s1][s2] + (a[s1] != c[s3]));
            }
            if (s2 < len_b && s3 < len_c) {
                dp[s1][s2 + 1] = min(dp[s1][s2 + 1], dp[s1][s2] + (b[s2] != c[s3]));
            }
        }
    }

    return dp[len_a][len_b];
}

void solve() {
    int t;cin>>t;
    while(t--){
        string a, b, c;
    cin >> a >> b >> c;

    int result = findMinChanges(a, b, c);
    if (result == -1) {
        cout << "Impossible to merge" << endl;
    } else {
        cout << result << endl;
    }
    }
}
int main(){
    solve();
}