Problem: 2050E - Three Strings
Language: java

Solution 1 (runtime: 171 ms):
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.*;

public class q1 {
    static int MOD = (int) (1e9 + 7);
    static int INF = Integer.MIN_VALUE;
    static int INT_MAX = Integer.MAX_VALUE;

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }

        long nextLong() {
            return Long.parseLong(next());
        }

        double nextDouble() {
            return Double.parseDouble(next());
        }

        char nextChar() {
            return next().charAt(0);
        }

        String nextLine() {
            String str = "";
            try {
                if (st.hasMoreTokens()) {
                    str = st.nextToken("\n");
                } else {
                    str = br.readLine();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            return str;
        }
    }

    static long gcd(long a, long b) {
        return b == 0 ? a : gcd(b, a % b);
    }
    // Usage: Find the greatest common divisor of two numbers.
    // Time Complexity: O(log(min(a, b)))
    // Space Complexity: O(log(min(a, b))) [recursive stack]

    static long lcm(long a, long b) {
        return (a / gcd(a, b)) * b;
    }
    // Usage: Find the least common multiple of two numbers.
    // Time Complexity: O(log(min(a, b)))
    // Space Complexity: O(log(min(a, b))) [recursive stack]

    static long power(long a, long b, long m) {
        long res = 1;
        a %= m;
        while (b > 0) {
            if ((b & 1) == 1)
                res = (res * a) % m;
            a = (a * a) % m;
            b >>= 1;
        }
        return res;
    }
    // Usage: Calculate (a^b) % m efficiently.
    // Time Complexity: O(log(b))
    // Space Complexity: O(1)

    static long modInverse(long a, long m) {
        return power(a, m - 2, m); // Fermat's Little Theorem
    }

    // ETF(Euler Totient function) ϕ(n).
    // find all unique prime factors of n
    // then do the below job.
    // Time Complexity:O(root N)
    // Space Complexity:O(1)
    static int phi(int n) {
        int result = n; // Start with n
        for (int p = 2; p * p <= n; p++) {
            // Check if p is a prime factor of n
            if (n % p == 0) {
                // If p is a factor, divide n by p repeatedly
                while (n % p == 0) {
                    n /= p;
                }
                // Apply the formula
                result -= result / p;
            }
        }
        // If n is still greater than 1, it must be a prime factor
        if (n > 1) {
            result -= result / n;
        }
        return result;
    }
    // Euler's theorm
    // a^ϕ(n)≡1 (mod n)
    // (a^b)%m≡(a^(b%ϕ(m)))%m :given a and m are co-prime(gcd(a,m)=1)

    // justsmall->largest index i such that arr[i] ≤ x.
    // justgreat->smallest index i such that arr[i] >=x.
    // time complexity:O(log(n))
    static int binarySearch(long[] arr, long x, boolean findJustSmall, boolean findJustGreat) {
        int left = 0, right = arr.length - 1, result = -1;

        while (left <= right) {
            int mid = left + (right - left) / 2;

            if (arr[mid] == x) {
                if (!findJustSmall && !findJustGreat)
                    return mid; // Exact match
                // Continue for justSmall/justGreat logic
            }

            if (findJustSmall) {
                if (arr[mid] <= x) {
                    result = mid; // Last valid index where arr[mid] <= x
                    left = mid + 1;
                } else {
                    right = mid - 1;
                }
            } else if (findJustGreat) {
                if (arr[mid] >= x) {
                    result = mid; // First valid index where arr[mid] >= x
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
            } else {
                if (arr[mid] < x) {
                    left = mid + 1;
                } else {
                    right = mid - 1;
                }
            }
        }

        return result;
    }

    // Sieve of Eratosthenes
    // Usage: Find all prime numbers up to n using the sieve of Eratosthenes.
    // Time Complexity: O(n log log n)
    // Space Complexity: O(n)
    static boolean[] primeSieve(int n) {
        boolean[] isPrime = new boolean[n + 1];
        Arrays.fill(isPrime, true);
        isPrime[0] = isPrime[1] = false;
        for (int i = 2; i * i <= n; i++) {
            if (isPrime[i]) {
                for (int j = i * i; j <= n; j += i) {
                    isPrime[j] = false;
                }
            }
        }
        return isPrime;
    }

    static boolean isPrimeSquare(long n, boolean[] isPrime) {
        if (n < 4)
            return false; // Smallest prime square is 2^2 = 4

        long s = (long) Math.sqrt(n);
        if (s * s != n)
            return false; // Check if n is a perfect square
        if (s > isPrime.length - 1)
            return false; // Ensure s is within precomputed range
        return isPrime[(int) s]; // Check if sqrt(n) is a prime
    }

    // Usage: Precompute cumulative sums for quick subarray sum queries
    // Time Complexity: O(n), where n is the size of the array
    // Space Complexity: O(n) for the prefix array
    static long[] prefixSum(long[] arr) {
        long[] prefix = new long[arr.length + 1];
        for (int i = 1; i <= arr.length; i++) {
            prefix[i] = prefix[i - 1] + arr[i - 1];
        }
        return prefix;
    }

    // Usage: Precompute cumulative sums for 2D matrices to quickly find the sum of
    // any submatrix
    // Time Complexity: O(n * m), where n is the number of rows and m is the number
    // of columns
    // Space Complexity: O(n * m) for the prefix array
    static long[][] prefixSum2D(long[][] matrix) {
        int n = matrix.length, m = matrix[0].length;
        long[][] prefix = new long[n + 1][m + 1];
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                prefix[i][j] = matrix[i - 1][j - 1]
                        + prefix[i - 1][j]
                        + prefix[i][j - 1]
                        - prefix[i - 1][j - 1];
            }
        }
        return prefix;
    }

    static long[] factorial;
    static long[] inverseFactorial;
    static void precomputeFactorials(int n, int mod) {
        factorial = new long[n + 1];
        inverseFactorial = new long[n + 1];
        factorial[0] = inverseFactorial[0] = 1;
        for (int i = 1; i <= n; i++) {
            factorial[i] = factorial[i - 1] * i % mod;
        }
        inverseFactorial[n] = power(factorial[n], mod - 2, mod);
        for (int i = n - 1; i >= 1; i--) {
            inverseFactorial[i] = inverseFactorial[i + 1] * (i + 1) % mod;
        }
    }
    static long nCr(int n, int r, int mod) {
        if (r > n || n < 0 || r < 0)
            return 0;
        return factorial[n] * inverseFactorial[r] % mod * inverseFactorial[n - r] % mod;
    }
    // Usage: Calculate nCr % mod (combination formula).
    // Precompute factorials using precomputeFactorials().
    // Time Complexity: O(log(mod)) per query (after precomputation).
    // Space Complexity: O(n) for factorial arrays.

    static long maxSubarraySum(long[] arr, int k) {
        long maxSum = Long.MIN_VALUE, windowSum = 0;
        for (int i = 0; i < arr.length; i++) {
            windowSum += arr[i];
            if (i >= k - 1) {
                maxSum = Math.max(maxSum, windowSum);
                windowSum -= arr[i - (k - 1)];
            }
        }
        return maxSum;
    }
    // Usage: Find the maximum sum of any subarray of size k.
    // Time Complexity: O(n)
    // Space Complexity: O(1)

    static int longestSubarrayWithSumAtMostK(int[] arr, int k) {
        int left = 0, sum = 0, maxLength = 0;
        for (int right = 0; right < arr.length; right++) {
            sum += arr[right];
            while (sum > k) {
                sum -= arr[left++];
            }
            maxLength = Math.max(maxLength, right - left + 1);
        }
        return maxLength;
    }
    // Usage: Find the length of the longest subarray with sum at most k.
    // Time Complexity: O(n)
    // Space Complexity: O(1)

    // Fenwick Tree (BIT)
    // Usage: Efficiently handle range sum queries and point updates
    // Time Complexity: O(log n) for both update and query operations
    // Space Complexity: O(n), where n is the size of the Fenwick tree
    static class FenwickTree {
        private long[] tree;

        FenwickTree(int n) {
            tree = new long[n + 1];
        }

        void update(int index, long value) {
            while (index < tree.length) {
                tree[index] += value;
                index += index & -index;
            }
        }

        long query(int index) {
            long sum = 0;
            while (index > 0) {
                sum += tree[index];
                index -= index & -index;
            }
            return sum;
        }

        long rangeQuery(int left, int right) {
            return query(right) - query(left - 1);
        }
    }

    // Segment Tree (Range Queries)
    // Usage: Handle range queries (sum, min, max) and point updates efficiently
    // Time Complexity: O(n) for construction, O(log n) for queries and updates
    // Space Complexity: O(n) for the input array and O(4 * n) for the segment tree
    static class SegmentTree {
        private long[] tree, arr;
        private int n;

        SegmentTree(long[] input) {
            n = input.length;
            arr = Arrays.copyOf(input, n);
            tree = new long[4 * n];
            build(0, 0, n - 1);
        }

        private void build(int node, int start, int end) {
            if (start == end) {
                tree[node] = arr[start];
            } else {
                int mid = (start + end) / 2;
                build(2 * node + 1, start, mid);
                build(2 * node + 2, mid + 1, end);
                tree[node] = merge(tree[2 * node + 1], tree[2 * node + 2]);
            }
        }

        private long merge(long left, long right) {
            return Math.min(left, right); // Replace with logic (min, max, sum)
        }

        public void update(int index, long value) {
            update(0, 0, n - 1, index, value);
        }

        private void update(int node, int start, int end, int index, long value) {
            if (start == end) {
                tree[node] = value;
            } else {
                int mid = (start + end) / 2;
                if (index <= mid) {
                    update(2 * node + 1, start, mid, index, value);
                } else {
                    update(2 * node + 2, mid + 1, end, index, value);
                }
                tree[node] = merge(tree[2 * node + 1], tree[2 * node + 2]);
            }
        }

        public long query(int left, int right) {
            return query(0, 0, n - 1, left, right);
        }

        private long query(int node, int start, int end, int left, int right) {
            if (start > right || end < left)
                return INF;
            if (start >= left && end <= right)
                return tree[node];
            int mid = (start + end) / 2;
            long lQuery = query(2 * node + 1, start, mid, left, right);
            long rQuery = query(2 * node + 2, mid + 1, end, left, right);
            return merge(lQuery, rQuery);
        }
    }


//implementation of Trie data structure
//L-length of the word in each function
//n->number of words inserted
//T.c->O(L) for all functions
//S.c->O(n*L*26) for all functions
    class Trie {
        // TrieNode class to represent each node in the Trie
         class TrieNode {
            TrieNode[] children;
            boolean isEndOfWord;
    
            public TrieNode() {
                children = new TrieNode[26]; // Supports only lowercase 'a' to 'z'
                isEndOfWord = false;
            }
        }
    
        private TrieNode root;
    
        // Constructor
        public Trie() {
            root = new TrieNode();
        }
    
        // Insert a word into the Trie
        public void insert(String word) {
            TrieNode node = root;
            for (char ch : word.toCharArray()) {
                int index = ch - 'a'; // Map 'a' to 0, 'z' to 25
                if (node.children[index] == null) {
                    node.children[index] = new TrieNode();
                }
                node = node.children[index];
            }
            node.isEndOfWord = true;
        }
    
        // Check if a word exists in the Trie
        public boolean search(String word) {
            TrieNode node = root;
            for (char ch : word.toCharArray()) {
                int index = ch - 'a';
                if (node.children[index] == null) {
                    return false;
                }
                node = node.children[index];
            }
            return node.isEndOfWord;
        }
    
        // Check if a prefix exists in the Trie
        public boolean startsWith(String prefix) {
            TrieNode node = root;
            for (char ch : prefix.toCharArray()) {
                int index = ch - 'a';
                if (node.children[index] == null) {
                    return false;
                }
                node = node.children[index];
            }
            return true;
        }
    }
    




     // T.C. = O(n) -> We traverse the array once.
    // S.C. = O(1) -> We use only a few variables.
    static int maxSubarraySum(int[] arr) {
        int maxSoFar = arr[0]; // Maximum sum found so far
        int currentMax = arr[0]; // Maximum sum ending at the current position

        for (int i = 1; i < arr.length; i++) {
            currentMax = Math.max(arr[i], currentMax + arr[i]); // Include or reset
            maxSoFar = Math.max(maxSoFar, currentMax); // Update max
        }

        return maxSoFar;
    }

    // 2-d kadane
    // T.C. = O(n^3) -> Outer loop (O(n)) for top row,
    // Inner loop (O(n)) for bottom row,
    // Applying 1D Kadane (O(n)) on each row combination.
    // S.C. = O(n) -> Space used by the temp array for column sums.
    static int maxSubmatrixSum(int[][] matrix) {
        int rows = matrix.length;
        int cols = matrix[0].length;
        int maxSum = Integer.MIN_VALUE;

        // Iterate over all possible top rows
        for (int top = 0; top < rows; top++) {
            int[] temp = new int[cols]; // Temporary array for column sums

            // Sum over all rows from `top` to `bottom`
            for (int bottom = top; bottom < rows; bottom++) {
                for (int col = 0; col < cols; col++) {
                    temp[col] += matrix[bottom][col];
                }

                // Apply 1D Kadane's on the column sums array
                int currentMax = maxSubarraySum(temp);
                maxSum = Math.max(maxSum, currentMax); // Update global max
            }
        }

        return maxSum;
    }

    // Graph Algorithms
    // Usage: Compute shortest paths between all pairs of nodes in a graph
    // Time Complexity: O(n^3), where n is the number of nodes
    // Space Complexity: O(n^2) for the distance matrix
    static void floydWarshall(long[][] dist) {
        int n = dist.length;
        for (int k = 0; k < n; k++) {
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    dist[i][j] = Math.min(dist[i][j], dist[i][k] + dist[k][j]);
                }
            }
        }
    }
    //prim's algo 
    //O(ElogV)
    static int primMST(int n, List<List<Edge1>> adj) {
        boolean[] visited = new boolean[n];
        PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[1] - b[1]); // {node, weight}
        pq.add(new int[]{0, 0}); // Start with node 0
        int mstCost = 0;

        while (!pq.isEmpty()) {
            int[] current = pq.poll();
            int node = current[0], weight = current[1];
            if (visited[node]) continue;

            visited[node] = true;
            mstCost += weight;

            for (Edge1 neighbor : adj.get(node)) {
                if (!visited[neighbor.node]) {
                    pq.add(new int[]{neighbor.node, neighbor.weight});
                }
            }
        }

        return mstCost;
    }


    //kruskal algorithm
    //O(ElogV)
    static int kruskalMST(int n, List<Edge> edges) {
        // Sort edges by weight
        edges.sort((a, b) -> a.weight - b.weight);

        int[] parent = new int[n];
        int[] rank = new int[n];
        for (int i = 0; i < n; i++) parent[i] = i;

        int mstCost = 0;
        int edgeCount = 0;

        for (Edge edge : edges) {
            if (find(parent, edge.src) != find(parent, edge.dest)) {
                mstCost += edge.weight;
                union(parent, rank, edge.src, edge.dest);
                edgeCount++;
                if (edgeCount == n - 1) break;
            }
        }

        return mstCost;
    }
    static int find(int[] parent, int x) {
        if (parent[x] != x) {
            parent[x] = find(parent, parent[x]); // Path compression
        }
        return parent[x];
    }
    static void union(int[] parent, int[] rank, int x, int y) {
        int rootX = find(parent, x);
        int rootY = find(parent, y);
    
        if (rootX != rootY) {
            if (rank[rootX] > rank[rootY]) {
                parent[rootY] = rootX; // Attach smaller tree under larger tree
            } else if (rank[rootX] < rank[rootY]) {
                parent[rootX] = rootY;
            } else {
                parent[rootY] = rootX;
                rank[rootX]++; // Increase rank only if they were equal
            }
        }
    }
        




   

    // Usage: Find shortest paths from a start node to all other nodes in a weighted
    // graph
    // Time Complexity: O((n + e) log n), where n is the number of nodes and e is
    // the number of edges
    // Space Complexity: O(n + e) for the graph representation
    static long[] dijkstra(int start, ArrayList<int[]>[] graph, int n) {
        long[] dist = new long[n];
        Arrays.fill(dist, INF);
        dist[start] = 0;
        PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingLong(a -> a[1]));
        pq.add(new int[] { start, 0 });

        while (!pq.isEmpty()) {
            int[] curr = pq.poll();
            int node = curr[0];
            long d = curr[1];
            if (d > dist[node])
                continue;

            for (int[] edge : graph[node]) {
                int next = edge[0], weight = edge[1];
                if (dist[node] + weight < dist[next]) {
                    dist[next] = dist[node] + weight;
                    pq.add(new int[] { next, (int) dist[next] });
                }
            }
        }
        return dist;
    }
    //like dijkstra
    //o(v*e)
    static int[] bellmanFord(int vertices, int edges, List<Edge> edgeList, int source) {
        int[] distance = new int[vertices];
        Arrays.fill(distance, Integer.MAX_VALUE);
        distance[source] = 0;
        for (int i = 1; i < vertices; i++) {
            for (Edge edge : edgeList) {
                if (distance[edge.src] != Integer.MAX_VALUE && distance[edge.src] + edge.weight < distance[edge.dest]) {
                    distance[edge.dest] = distance[edge.src] + edge.weight;
                }
            }
        }
        for (Edge edge : edgeList) {
            if (distance[edge.src] != Integer.MAX_VALUE && distance[edge.src] + edge.weight < distance[edge.dest]) {
                return null; 
            }
        }
        return distance; 
    }


    //bipartite check
    static boolean isBipartite(int vertices, List<List<Integer>> graph) {
        int[] colors = new int[vertices];
        Arrays.fill(colors, -1); // -1 indicates unvisited nodes

        // Iterate over all components of the graph
        for (int i = 0; i < vertices; i++) {
            if (colors[i] == -1) { // Unvisited node
                if (!bfsCheck(i, graph, colors)) {
                    return false;
                }
            }
        }
        return true;
    }

    private static boolean bfsCheck(int start, List<List<Integer>> graph, int[] colors) {
        Queue<Integer> queue = new LinkedList<>();
        queue.add(start);
        colors[start] = 0; // Start with color 0

        while (!queue.isEmpty()) {
            int node = queue.poll();
            for (int neighbor : graph.get(node)) {
                if (colors[neighbor] == -1) {
                    // Assign the opposite color to the neighbor
                    colors[neighbor] = 1 - colors[node];
                    queue.add(neighbor);
                } else if (colors[neighbor] == colors[node]) {
                    // If the neighbor has the same color, it's not bipartite
                    return false;
                }
            }
        }
        return true;
    }
    

    // -------------------- KMP Algorithm --------------------
    // Usage: Precompute LPS array for efficient pattern matching in strings
    // Time Complexity: O(n), where n is the length of the pattern
    // Space Complexity: O(n) for the LPS array
    static int[] kmp(String pattern) {
        int n = pattern.length();
        int[] lps = new int[n];
        int len = 0, i = 1;
        while (i < n) {
            if (pattern.charAt(i) == pattern.charAt(len)) {
                lps[i++] = ++len;
            } else if (len > 0) {
                len = lps[len - 1];
            } else {
                lps[i++] = 0;
            }
        }
        return lps;
    }

    // Usage: Find all occurrences of a pattern in a text using the KMP algorithm
    // Time Complexity: O(n + m), where n is the length of the text and m is the
    // length of the pattern
    // Space Complexity: O(m) for the LPS array
    static List<Integer> kmpSearch(String text, String pattern) {
        int[] lps = kmp(pattern);
        List<Integer> result = new ArrayList<>();
        int i = 0, j = 0;
        while (i < text.length()) {
            if (text.charAt(i) == pattern.charAt(j)) {
                i++;
                j++;
            }
            if (j == pattern.length()) {
                result.add(i - j);
                j = lps[j - 1];
            } else if (i < text.length() && text.charAt(i) != pattern.charAt(j)) {
                if (j > 0) {
                    j = lps[j - 1];
                } else {
                    i++;
                }
            }
        }
        return result;
    }

    // -------------------- Rabin-Karp Algorithm --------------------
    // Usage: Find all occurrences of a pattern in a text using Rabin-Karp algorithm
    // Time Complexity: O(n + m) on average, O(n * m) in worst case due to hash
    // collisions
    // Space Complexity: O(1) additional space for rolling hash
    private final static int prime = 31;

    static int patternSearch(char[] text, char[] pattern) {
        int m = pattern.length;
        int n = text.length;

        long patternHash = createHash(pattern, m - 1);
        long textHash = createHash(text, m - 1);

        for (int i = 1; i <= n - m + 1; i++) {
            if (patternHash == textHash && checkEqual(text, i - 1, i + m - 2, pattern, 0, m - 1)) {
                return i - 1;
            }
            if (i < n - m + 1) {
                textHash = recalculateHash(text, i - 1, i + m - 1, textHash, m);
            }
        }

        return -1;
    }

    static long recalculateHash(char[] str, int oldIndex, int newIndex, long oldHash, int patternLen) {
        long newHash = oldHash - str[oldIndex];
        newHash /= prime; // Adjust the hash for the next window
        newHash += str[newIndex] * Math.pow(prime, patternLen - 1); // Add new character to hash
        return newHash;
    }

    static long createHash(char[] str, int end) {
        long hash = 0;
        for (int i = 0; i <= end; i++) {
            hash += str[i] * Math.pow(prime, i); // Calculate hash using powers of prime
        }
        return hash;
    }

    static boolean checkEqual(char[] str1, int start1, int end1, char[] str2, int start2, int end2) {
        if (end1 - start1 != end2 - start2) {
            return false;
        }
        while (start1 <= end1 && start2 <= end2) {
            if (str1[start1] != str2[start2]) {
                return false;
            }
            start1++;
            start2++;
        }
        return true;
    }

    // -------------------- Z-Algorithm --------------------
    // Usage: Precompute Z array for efficient pattern matching in strings
    // Time Complexity: O(n), where n is the length of the combined string
    // Space Complexity: O(n) for the Z array
    static int[] zAlgorithm(String s) {
        int n = s.length();
        int[] z = new int[n];
        int l = 0, r = 0;
        for (int i = 1; i < n; i++) {
            if (i <= r) {
                z[i] = Math.min(r - i + 1, z[i - l]);
            }
            while (i + z[i] < n && s.charAt(z[i]) == s.charAt(i + z[i])) {
                z[i]++;
            }
            if (i + z[i] - 1 > r) {
                l = i;
                r = i + z[i] - 1;
            }
        }
        return z;
    }

    // Usage: Find all occurrences of a pattern in a text using Z-Algorithm
    // Time Complexity: O(n + m), where n is the length of the text and m is the
    // length of the pattern
    // Space Complexity: O(n + m) for the concatenated string and Z array
    static List<Integer> zSearch(String text, String pattern) {
        String combined = pattern + "$" + text; // "$" acts as a separator
        int[] z = zAlgorithm(combined);
        List<Integer> result = new ArrayList<>();
        for (int i = pattern.length() + 1; i < combined.length(); i++) {
            if (z[i] == pattern.length()) {
                result.add(i - pattern.length() - 1);
            }
        }
        return result;
    }
    //O(n) for finding next greater element of every element of array
     static int[] nextGreater(int[] arr) {
        int n = arr.length;
        int[] result = new int[n];
        Stack<Integer> stack = new Stack<>();
        for (int i = n - 1; i >= 0; i--) {
            while (!stack.isEmpty() && stack.peek() <= arr[i]) {
                stack.pop();
            }
            result[i] = stack.isEmpty() ? -1 : stack.peek();
            stack.push(arr[i]);
        }
        return result;
    }
   //O(n) for finding next smaller element of every element of array
    static int[] nextSmaller(int[] arr) {
        int n = arr.length;
        int[] result = new int[n];
        Stack<Integer> stack = new Stack<>();

        for (int i = n - 1; i >= 0; i--) {
            while (!stack.isEmpty() && stack.peek() >= arr[i]) {
                stack.pop();
            }
            result[i] = stack.isEmpty() ? -1 : stack.peek();
            stack.push(arr[i]);
        }

        return result;
    }



//sparse table for static arrays for handling range
//T.c->O(nlogn)[for creating table]
//range query->O(1)
class SparseTable {
    private int[][] st; // Sparse table
    private int[] log;  // Logarithm values
    private int n;      // Array size

    // Constructor: Build the Sparse Table
    public SparseTable(int[] arr) {
        this.n = arr.length;
        int maxLog = (int) (Math.log(n) / Math.log(2)) + 1;

        // Initialize Sparse Table and log array
        st = new int[n][maxLog];
        log = new int[n + 1];

        // Compute log values for fast access
        for (int i = 2; i <= n; i++) {
            log[i] = log[i / 2] + 1;
        }

        // Fill the first column of Sparse Table
        for (int i = 0; i < n; i++) {
            st[i][0] = arr[i];
        }

        // Fill the rest of the Sparse Table
        for (int j = 1; (1 << j) <= n; j++) {
            for (int i = 0; i + (1 << j) - 1 < n; i++) {
                st[i][j] = Math.min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);//this line is changed for handling different problem 
            }
        }
    }
    public int query(int L, int R) {
        int j = log[R - L + 1];
        return Math.min(st[L][j], st[R - (1 << j) + 1][j]);//this line is changed for handling different problem 
                                   //L+(R-L+1)-2^j
    }
}




    // modulo factorial
    static long fact(long n) {
        long ans = 1;
        long m = (long) (1e9 + 7);
        for (long i = 1; i <= n; i++)
            ans = (ans * i) % m;
        return ans;
    }
    static int inversions(char[] s1, char[] s2, char[] s) {
        int n = s1.length, m = s2.length;
        int[][] dp = new int[n + 1][m + 1];
        for (int[] row : dp) {
            Arrays.fill(row, Integer.MAX_VALUE);
        }
        dp[0][0] = 0;
        for (int i = 0; i <= n; i++) {
            for (int j = 0; j <= m; j++) {
                int k = i + j;
                if (k >= s.length) continue;
                if (i < n && s[k] == s1[i]) {
                    dp[i + 1][j] = Math.min(dp[i + 1][j], dp[i][j]);
                }
                if (j < m && s[k] == s2[j]) {
                    dp[i][j + 1] = Math.min(dp[i][j + 1], dp[i][j]);
                }
                if (i < n) {
                    dp[i + 1][j] = Math.min(dp[i + 1][j], dp[i][j] + 1);
                }
                if (j < m) {
                    dp[i][j + 1] = Math.min(dp[i][j + 1], dp[i][j] + 1);
                }
            }
        }

        return dp[n][m];
    }


    public static void main(String[] args) throws IOException {
        OutputStream outputStream = System.out;
        PrintWriter out = new PrintWriter(outputStream);
        FastReader sc = new FastReader();
        int totalTests = sc.nextInt();
        while (totalTests-- > 0) {
char s1[]=sc.next().toCharArray();
char s2[]=sc.next().toCharArray();
char s[]=sc.next().toCharArray();
System.out.println(inversions(s1,s2,s));



















    }
    }
}

class Pair<F, S> {
    F first;
    S second;

    public Pair(F first, S second) {
        this.first = first;
        this.second = second;
    }
}

class Triplet<F, S, T> {
    F first;
    S second;
    T third;

    public Triplet(F first, S second, T third) {
        this.first = first;
        this.second = second;
        this.third = third;
    }
}

class Edge {
    public int src;
    public int dest;
    public int weight;

    public Edge(int src, int dest, int weight) {
        this.src = src;
        this.dest = dest;
        this.weight = weight;
    }
}
class Edge1 {
    int node, weight;

    Edge1(int node, int weight) {
        this.node = node;
        this.weight = weight;
    }
}


Solution 2 (runtime: 187 ms):
import java.util.*;

import java.io.*;

import java.math.*;

import static java.lang.Math.*;



public class Main {

    static Reader sc = new Reader();

    static PrintWriter out = new PrintWriter(System.out);

    static Debug dbg = new Debug();

    static int mod = (int) (1000000007); //998244353 1000000007;

    static long hash_mod = 92233720368547753L;

    static ArrayList<ArrayList<Integer>> adj;

    static int subtree[];

    static long pref[];





    /***Code Starts From Here***/

    public static void main(String[] args) throws IOException {

        READING(); /*→→→[■□□□□□□□□□] →→→ [■■■■□□□□□□] →→→  [■■■■■■■□□□] →→→ [■■■■■■■■■□]*/ ERROR();

        //preprocess();

        // int t = 1;

        int t = sc.nextInt();

        while (t-->0) sudo_su();

        sc.close();

        out.flush();

    }



    public static void sudo_su() throws IOException {

        String a = sc.next(), b = sc.next(), c = sc.next();

        int dp[][] = new int[a.length() + 1][b.length() + 1];

        for (int current[] : dp) Arrays.fill(current, -1);

        int res = solve(0, 0, a, b, c, dp);

        out.println(res);

    }

    static int solve(int i, int j, String a, String b, String c, int dp[][]) {

        if (i >= a.length() && j >= b.length()) return 0;

        if ( (i >= a.length() && j < b.length() ) || (j >= b.length() && i < a.length())) {

            if (i >= a.length()) {

                int add = 0;

                if (c.charAt(i + j) != b.charAt(j)) add++;

                return dp[i][j] = add + solve(i, j + 1, a, b, c, dp);

            }

            else if (j >= b.length()) {

                int add = 0;

                if (c.charAt(i + j) != a.charAt(i)) add++;

                return dp[i][j] = add + solve(i + 1, j, a, b, c, dp);

            }

        }

        if (dp[i][j] != -1) return dp[i][j];

        int op1 = Integer.MAX_VALUE / 10, op2 = Integer.MAX_VALUE / 10;

        int add = 0;

        if (c.charAt(i + j) != a.charAt(i)) add++;

        op1 = add + solve(i + 1, j, a, b, c, dp);



        add = 0;

        if (c.charAt(i + j) != b.charAt(j)) add++;

        op2 = add + solve(i, j + 1, a, b, c, dp);

        return dp[i][j] = min(op1, op2);

    }





    /**Code Ends Here (No Need To Scroll Down)**/

    static class Reader {

        final private int BUFFER_SIZE = 1 << 16;

        private DataInputStream din;

        BufferedReader reader;

        private byte[] buffer;

        private int bufferPointer, bytesRead;

        public Reader() {

            din = new DataInputStream(System.in);

            buffer = new byte[BUFFER_SIZE];

            bufferPointer = bytesRead = 0;

        }

        public Reader(String file_name) throws IOException {

            din = new DataInputStream(new FileInputStream(file_name));

            buffer = new byte[BUFFER_SIZE];

            bufferPointer = bytesRead = 0;

        }

        public String readLine() throws IOException {

            byte[] buf = new byte[64];

            int cnt = 0, c;

            while ((c = read()) != -1) {

                if (c == '\n') break;

                buf[cnt++] = (byte) c;

            }

            return new String(buf, 0, cnt);

        }

        public int nextInt() throws IOException {

            int ret = 0;

            byte c = read();

            while (c <= ' ') c = read();

            boolean neg = (c == '-');

            if (neg) c = read();

            do {ret = ret * 10 + c - '0';}

            while ((c = read()) >= '0' && c <= '9');

            if (neg) return -ret;

            return ret;

        }

        public long nextLong() throws IOException {

            long ret = 0;

            byte c = read();

            while (c <= ' ') c = read();

            boolean neg = (c == '-');

            if (neg) c = read();

            do {ret = ret * 10L + c - '0';}

            while ((c = read()) >= '0' && c <= '9');

            if (neg) return -ret;

            return ret;

        }

        public double nextDouble() throws IOException {

            double ret = 0, div = 1;

            byte c = read();

            while (c <= ' ') c = read();

            boolean neg = (c == '-');

            if (neg) c = read();

            do {ret = ret * 10 + c - '0';}

            while ((c = read()) >= '0' && c <= '9');

            if (c == '.') while ((c = read()) >= '0' && c <= '9') ret += (c - '0') / (div *= 10);

            if (neg) return -ret;

            return ret;

        }

        private void fillBuffer() throws IOException {

            bytesRead = din.read(buffer, bufferPointer = 0, BUFFER_SIZE);

            if (bytesRead == -1) buffer[0] = -1;

        }

        private byte read() throws IOException {

            if (bufferPointer == bytesRead) fillBuffer();

            return buffer[bufferPointer++];

        }

        public String next() throws IOException {

            StringBuilder sb = new StringBuilder();

            byte c;

            while ((c = read()) <= ' ') ;

            do {sb.append((char) c);}

            while ((c = read()) > ' ');

            return sb.toString();

        }

        public int nextInt2() throws IOException {

            int ret = 0;

            byte c = read();

            while (c <= ' ') c = read();

            boolean neg = (c == '-');

            if (neg) c = read();

            do {ret = ret * 10 + c - '0';}

            while ((c = read()) >= '0' && c <= '9');

            if (neg) return -ret;

            return ret;

        }

        public void close() throws IOException {

            if (din == null) return;

            din.close();

        }

    }



    static long fast_pow(long a, long p, long mod) {

        long res = 1;

        while (p > 0) {

            if (p % 2 == 0) {

                a = ((a % mod) * (a % mod)) % mod;

                p /= 2;

            }

            else {

                res = ((res % mod) * (a % mod)) % mod;

                p--;

            }

        }

        return res;

    }

    static long exp(long base, long exp) {

        if (exp == 0) return 1;

        long half = exp(base, exp / 2);

        if (exp % 2 == 0) return mul(half, half);

        return mul(half, mul(half, base));

    }



    //Factorials and Inverse Factorials;

    static long[] factorials = new long[2_000_001];

    static long[] invFactorials = new long[2_000_001];

    static boolean[] isPrime;

    static int[] smallestFactorOf;

    static void precompFacts() {

        factorials[0] = invFactorials[0] = 1;

        for (int i = 1; i < factorials.length; i++)

            factorials[i] = mul(factorials[i - 1], i);

        invFactorials[factorials.length - 1] = exp(factorials[factorials.length - 1], mod - 2);

        for (int i = invFactorials.length - 2; i >= 0; i--)

            invFactorials[i] = mul(invFactorials[i + 1], i + 1);

    }

    static long nCk(int n, int k) {

        //use precompFacts first;

        return mul(factorials[n], mul(invFactorials[k], invFactorials[n - k]));

    }



    //Prime Generator;

    static void Generate_Primes(int upto) {

        // Sieve of Eratosthenes:

        isPrime = new boolean[upto + 1];

        smallestFactorOf = new int[upto + 1];

        Arrays.fill(smallestFactorOf, 1);

        Arrays.fill(isPrime, true);

        isPrime[1] = isPrime[0] = false;

        for (long i = 2; i < upto + 1; i++) {

            if (isPrime[(int) i]) {

                smallestFactorOf[(int) i] = (int) i;

                // Mark all the muresiples greater than or equal

                // to the square of i to be false.

                for (long j = i; j * i < upto + 1; j++) {

                    if (isPrime[(int) j * (int) i]) {

                        isPrime[(int) j * (int) i] = false;

                        smallestFactorOf[(int) j * (int) i] = (int) i;

                    }

                }

            }

        }

    }



    static long Div(long x, long y) {return mul(x, modinv(y));}

    static long LCM(long a, long b) {return (a / GCD(a, b)) * b;}

    static long modinv(long x) {return fast_pow(x, mod - 2, mod);}

    static long add(long a, long b) {a += b; if (a >= mod) a-= mod; return a;}

    static long mod(long a, long b) {long r = a % b;return r < 0 ? r + b : r;}    

    static long GCD(long x, long y) {if(y == 0) return x;return GCD(y, x % y);}

    static long sub(long x, long y) {long z = x - y; if (z < 0)  z += mod;return z;}

    static long mul(long a, long b) {return (long) ((long) ((a % mod) * 1L * (b % mod)) % mod);}

    public static void READING(){if(System.getProperty("ONLINE_JUDGE") == null){try{sc = new Reader("input.txt");out = new PrintWriter("output.txt");}catch (Exception e){}}}

    public static void ERROR() {try {PrintStream fileOut = new PrintStream(new FileOutputStream("dbg.txt", false), true, "UTF-8");System.setErr(fileOut);} catch (FileNotFoundException | UnsupportedEncodingException e) {e.printStackTrace();}}



    public static void sort(int[] arr) {

        //because Arrays.sort() uses quicksort which is dumb

        //Collections.sort() uses merge sort

        ArrayList<Integer> ls = new ArrayList<>();

        for (Integer x : arr) ls.add(x);

        Collections.sort(ls);

        for (int i = 0; i < arr.length; i++) arr[i] = ls.get(i);

    }



    public static void sort(long[] arr) {

        //because Arrays.sort() uses quicksort which is dumb

        //Collections.sort() uses merge sort

        ArrayList<Long> ls = new ArrayList<>();

        for (Long x : arr) ls.add(x);

        Collections.sort(ls);

        for (int i = 0; i < arr.length; i++) arr[i] = ls.get(i);

    }



    static class Unique_Pair {

        int first;

        int second;

        Unique_Pair(int first, int second) {

            this.first = first;

            this.second = second;

        }

        @Override

        public boolean equals(Object o) {

            if (this == o) return true;

            if (o == null || getClass() != o.getClass()) return false;

            Unique_Pair current = (Unique_Pair) (o);

            return first == current.first && second == current.second;

        }

        @Override

        public int hashCode() {

            return Objects.hash(first, second);

        }

        @Override

        public String toString() {

            return "(" + first + " " + second + ")";

        }

    }

    

    @SuppressWarnings("serial")

    static class CountMap<T> extends TreeMap<T, Integer>{

        CountMap() {}

        CountMap(T[] arr) {this.putCM(arr);}

        public Integer putCM(T key) {

            if (super.containsKey(key)) return super.put(key, super.get(key) + 1);

            else return super.put(key, 1);

        }

        public Integer removeCM(T key) {

            Integer count = super.get(key);

            if (count == null) return -1;

            if (count == 1) return super.remove(key);

            else  return super.put(key, super.get(key) - 1);

        }

        public Integer getCM(T key) {

            Integer count = super.get(key);

            if (count == null) return 0;

            return count;

        }

        public void putCM(T[] arr) {

            for (T ele : arr) this.putCM(ele);

        }

    }



    static class DSU {

        int[] Parent, Group_Size;

        int Number_of_Nodes, Number_of_Groups, Max_Group;

        public DSU(int Number_of_Nodes) {

            this.Number_of_Nodes = Number_of_Nodes;

            Parent = new int[Number_of_Nodes + 1];

            Group_Size = new int[Number_of_Nodes + 1];

            Number_of_Groups = Number_of_Nodes;

            Max_Group = 1;

            for (int i = 1; i <= Number_of_Nodes; i++) {

                Parent[i] = i;

                Group_Size[i] = 1;

            }

        }

        public int Leader(int x) {

            return Parent[x] = (Parent[x] == x ? x : Leader(Parent[x]));

        }

        public boolean Is_same_Group(int x, int y) {

            return Leader(x) == Leader(y);

        }

        public void unite(int x, int y) {

            int leader1 = Leader(x);

            int leader2 = Leader(y);

            if (leader1 != leader2) {

                Number_of_Groups--;

                if (Group_Size[leader1] < Group_Size[leader2]) {

                    int temp = leader1;

                    leader1 = leader2;

                    leader2 = temp;

                }

                Parent[leader2] = leader1;

                Group_Size[leader1] += Group_Size[leader2];

                Max_Group = Math.max(Max_Group, Group_Size[leader1]);

            }

        }

        public int getSize(int x) {

            return Group_Size[Leader(x)];

        }

    }



    static class Hashing {

        long[] hash1, hash2;

        long[] inv1, inv2;

        int n;

        static int muresiplier = 43;

        static final Random rnd = new Random();

        static final int mod1 = BigInteger.valueOf((int) (1e9 + rnd.nextInt((int) 1e9))).nextProbablePrime().intValue();

        static final int mod2 = BigInteger.valueOf((int) (1e9 + rnd.nextInt((int) 1e9))).nextProbablePrime().intValue();

        static final int invMuresiplier1 = BigInteger.valueOf(muresiplier).modInverse(BigInteger.valueOf(mod1)).intValue();

        static final int invMuresiplier2 = BigInteger.valueOf(muresiplier).modInverse(BigInteger.valueOf(mod2)).intValue();

        public Hashing(String s) {

            n = s.length();

            hash1 = new long[n + 1]; hash2 = new long[n + 1];

            inv1 = new long[n + 1]; inv2 = new long[n + 1];

            inv1[0] = 1; inv2[0] = 1;

            long p1 = 1; long p2 = 1;

            for (int i = 0; i < n; i++) {

                hash1[i + 1] = (hash1[i] + s.charAt(i) * p1) % mod1;

                p1 = p1 * muresiplier % mod1;

                inv1[i + 1] = inv1[i] * invMuresiplier1 % mod1;

                hash2[i + 1] = (hash2[i] + s.charAt(i) * p2) % mod2;

                p2 = p2 * muresiplier % mod2;

                inv2[i + 1] = inv2[i] * invMuresiplier2 % mod2;

            }

        }

        public long getHash(int i, int len) {

            return (((hash1[i + len] - hash1[i] + mod1) * inv1[i] % mod1) << 32) + (hash2[i + len] - hash2[i] + mod2) * inv2[i] % mod2;



        }

        public long getHashbounds(int x, int y) {

            return getHash(x, y - x + 1);

        }

    }



    static class Trie {

        class Node {

            Node[] children;

            boolean isEnd;

            Node() {

                children = new Node[26];

            }

        }

        Node root;

        Trie() {

            root = new Node();

        }

        public void insert(String word) {

            Node curr = root;

            for (char ch : word.toCharArray()) {

                if (curr.children[ch - 'a'] == null) curr.children[ch - 'a'] = new Node();

                curr = curr.children[ch - 'a'];

            }

            curr.isEnd = true;

        }

        public boolean find(String word) {

            Node curr = root;

            for (char ch : word.toCharArray()) {

                if (curr.children[ch - 'a'] == null)  return false;

                curr = curr.children[ch - 'a'];

            }

            return curr.isEnd;

        }

    }

    

    static class MultiSet<T> {

        TreeMap<T, Integer> frequency;

        TreeSet<T> set;

        int size;

        public MultiSet() {

            set = new TreeSet<>();

            frequency = new TreeMap<>();

            size = 0;

        }

        public MultiSet(Comparator<T> cmp) {

            set = new TreeSet<>(cmp);

            frequency = new TreeMap<>(cmp);

            size = 0;

        }

        public void add(T elem) {

            if (frequency.get(elem) == null || frequency.get(elem) == 0) {

                frequency.put(elem, 0);

                set.add(elem);

            }

            frequency.put(elem, frequency.get(elem) + 1);

            size++;

        }

        public void remove(T elem) {

            if (!set.contains(elem)) return;

            frequency.put(elem, frequency.get(elem) - 1);

            if (frequency.get(elem) == 0) {

                set.remove(elem);

                frequency.remove(elem);

            }

            size--;

        }

        public boolean contains(T elem) {

            return set.contains(elem);

        }

        @Override

        public String toString() {

            String current = "(";

            for (T ele : set) {

                int freq = frequency.get(ele);

                for (int i = 0; i < freq; i++) {

                    if (current.length() == 1) current += ele;

                    else current += "," + ele;

                }

            }

            current += ")";

            return current;

        }

        public int count(T element) {return frequency.getOrDefault(element, 0);}

        public int size() {int size = 0; for(int count : frequency.values()) size += count; return size;}

        public T ceiling(T element) {return frequency.ceilingKey(element);}

        public T floor(T element) {return frequency.floorKey(element);}

        public T higher(T element) {return frequency.higherKey(element);}

        public T lower(T element) { return frequency.lowerKey(element);}

        public T last() {

            return set.last();

        }

    }



    static class MultiTreeSet<E> { 

        TreeMap<E, Integer> freqTreeMap = new TreeMap<E, Integer>();

        int size;

        public MultiTreeSet() {}

        public MultiTreeSet(Collection<? extends E> c) {

            for (E element : c) add(element);

        }

        public int size() {return size;}

        public void add(E element) {

            Integer freq = freqTreeMap.get(element);

            if(freq==null) freqTreeMap.put(element, 1);

            else freqTreeMap.put(element,freq + 1);

            ++size;

        }

        public void remove(E element) {

            Integer freq = freqTreeMap.get(element);

            if(freq!=null) {

                if(freq == 1) freqTreeMap.remove(element);

                else freqTreeMap.put(element, freq-1);--size;

            }

        }

        public int get(E element) {

            Integer freq = freqTreeMap.get(element);

            if(freq == null) return 0;

            return freq;

        }

        public boolean contains(E element) {return get(element) > 0;}

        @Override 

        public String toString() {

            String current = "( ";

            for (E ele : freqTreeMap.keySet()) {

                int freq = freqTreeMap.get(ele);

                for (int i = 0; i < freq; i++) {

                    current += ele + " ";

                }

            }

            current += ")";

            return current;

        }

        public boolean isEmpty() {return size == 0;}

        public E first() {return freqTreeMap.firstKey();}

        public E last() {return freqTreeMap.lastKey();}

        public E ceiling(E element) {return freqTreeMap.ceilingKey(element);}

        public E floor(E element) {return freqTreeMap.floorKey(element);}

        public E higher(E element) {return freqTreeMap.higherKey(element);}

        public E lower(E element) {return freqTreeMap.lowerKey(element);}

    }

    

    static class Debug {

        public static boolean LOCAL = getLocal();

        public static boolean getLocal() {

            try {

                return System.getProperty("LOCAL") == null;

            }catch(SecurityException e) {

                return false;

            }

        }

        public static <T> String ts(T t) {

            if(t==null) {

                return "null";

            }

            if(t instanceof Iterable) {

                return ts((Iterable<?>) t);

            }else if(t instanceof int[]) {

                String s = Arrays.toString((int[]) t);

                return "{"+s.substring(1, s.length()-1)+"}";

            }else if(t instanceof long[]) {

                String s = Arrays.toString((long[]) t);

                return "{"+s.substring(1, s.length()-1)+"}";

            }else if(t instanceof char[]) {

                String s = Arrays.toString((char[]) t);

                return "{"+s.substring(1, s.length()-1)+"}";

            }else if(t instanceof double[]) {

                String s = Arrays.toString((double[]) t);

                return "{"+s.substring(1, s.length()-1)+"}";

            }else if(t instanceof boolean[]) {

                String s = Arrays.toString((boolean[]) t);

                return "{"+s.substring(1, s.length()-1)+"}";

            }else if(t instanceof Object[]) {

                return ts((Object[]) t);

            }

            return t.toString();

        }

        private static <T> String ts(T[] arr) {

            StringBuilder ret = new StringBuilder();

            ret.append("{");

            boolean first = true;

            for(T t: arr) {

                if(!first) ret.append(", ");

                first = false;

                ret.append(ts(t));

            }

            ret.append("}");

            return ret.toString();

        }

        private static <T> String ts(Iterable<T> iter) {

            StringBuilder ret = new StringBuilder();

            ret.append("{");

            boolean first = true;

            for(T t: iter) {

                if(!first) ret.append(", ");

                first = false;

                ret.append(ts(t));

            }

            ret.append("}");

            return ret.toString();

        }

        public static void print(Object... o) {

            if(LOCAL) {

                System.err.print("Line #"+Thread.currentThread().getStackTrace()[2].getLineNumber()+": [");

                for(int i = 0; i<o.length; i++) {

                    if(i!=0) System.err.print(", ");

                    System.err.print(ts(o[i]));

                }

                System.err.println("]");

            }

        }

    }

}