Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

555. Split Concatenated Strings - Leetcode Solution

Code Implementation

class Solution:
    def splitLoopedString(self, strs):
        # Preprocess: for each string, pick its lexicographically larger form (original or reversed)
        strs = [max(s, s[::-1]) for s in strs]
        ans = ''
        n = len(strs)
        for i in range(n):
            s = strs[i]
            rev = s[::-1]
            for candidate in [s, rev]:
                for k in range(len(candidate)):
                    # Form the new string by splitting at k, then concatenate rest
                    mid = candidate[k:] + candidate[:k]
                    rest = ''.join(strs[(i+1)%n:] + strs[:i])
                    temp = mid + rest
                    if temp > ans:
                        ans = temp
        return ans
      
class Solution {
public:
    string splitLoopedString(vector<string>& strs) {
        int n = strs.size();
        for (auto& s : strs) {
            string rev = s;
            reverse(rev.begin(), rev.end());
            if (rev > s) s = rev;
        }
        string ans = "";
        for (int i = 0; i < n; ++i) {
            string s = strs[i];
            string rev = s;
            reverse(rev.begin(), rev.end());
            for (const string& candidate : {s, rev}) {
                for (int k = 0; k < candidate.size(); ++k) {
                    string mid = candidate.substr(k) + candidate.substr(0, k);
                    string rest = "";
                    for (int j = (i + 1) % n; j != i; j = (j + 1) % n)
                        rest += strs[j];
                    string temp = mid + rest;
                    if (temp > ans) ans = temp;
                }
            }
        }
        return ans;
    }
};
      
class Solution {
    public String splitLoopedString(String[] strs) {
        int n = strs.length;
        for (int i = 0; i < n; ++i) {
            String rev = new StringBuilder(strs[i]).reverse().toString();
            if (rev.compareTo(strs[i]) > 0) strs[i] = rev;
        }
        String ans = "";
        for (int i = 0; i < n; ++i) {
            String s = strs[i];
            String rev = new StringBuilder(s).reverse().toString();
            for (String candidate : new String[]{s, rev}) {
                for (int k = 0; k < candidate.length(); ++k) {
                    String mid = candidate.substring(k) + candidate.substring(0, k);
                    StringBuilder rest = new StringBuilder();
                    for (int j = (i + 1) % n; j != i; j = (j + 1) % n)
                        rest.append(strs[j]);
                    String temp = mid + rest.toString();
                    if (temp.compareTo(ans) > 0) ans = temp;
                }
            }
        }
        return ans;
    }
}
      
var splitLoopedString = function(strs) {
    // Preprocess: maximize each string lexicographically
    strs = strs.map(s => {
        let rev = s.split('').reverse().join('');
        return rev > s ? rev : s;
    });
    let ans = '';
    let n = strs.length;
    for (let i = 0; i < n; ++i) {
        let s = strs[i];
        let rev = s.split('').reverse().join('');
        for (let candidate of [s, rev]) {
            for (let k = 0; k < candidate.length; ++k) {
                let mid = candidate.slice(k) + candidate.slice(0, k);
                let rest = '';
                for (let j = (i + 1) % n; j != i; j = (j + 1) % n)
                    rest += strs[j];
                let temp = mid + rest;
                if (temp > ans) ans = temp;
            }
        }
    }
    return ans;
};
      

Problem Description

Given a list of strings strs, imagine concatenating them in a loop (so the last connects back to the first). You can reverse any of the individual strings (or leave them as they are), but you must maintain their order in the list. After making your choices, you can "split" the loop at any position (i.e., pick any rotation of the concatenated string as the starting point). Your task is to find the lexicographically largest string that can be obtained in this way.

  • You can reverse each string independently or leave it unchanged.
  • The order of strings in the array must be preserved.
  • After processing, you can split the loop at any position (i.e., rotate the concatenated string).
  • Return the lexicographically largest string possible.

Thought Process

At first glance, the problem seems to require checking all combinations of string reversals and all possible split positions, which could be computationally expensive. A brute-force approach would involve generating every possible arrangement, but this quickly becomes infeasible as the list grows.

To optimize, we notice two key things:

  • For each string, only two forms matter: its original and its reversed version. For maximum effect, we can always choose the lexicographically larger one for every string, since only the split point changes the starting position.
  • After fixing the best version of each string, the only thing left is to try every possible split point in the loop and see which yields the largest string. Since the split can occur inside any string, and each string can be reversed or not, we must try splitting at every position in every possible orientation.
This leads us to a systematic approach: for each string, try both its original and reversed versions, and for each possible split within that string, build the resulting string and keep track of the maximum.

Solution Approach

  • Step 1: Preprocessing
    • For each string in strs, replace it with its lexicographically larger form (original or reversed). This ensures we always use the best possible version of each string.
  • Step 2: Try All Split Points
    • For each string in the list (let's call it strs[i]):
      • Consider both its original and reversed forms.
      • For each possible split position k in this string:
        • Split the string at position k, making the substring from k to end the start of the result, followed by the substring from start to k.
        • Concatenate the rest of the strings in their preprocessed forms, starting from strs[i+1] to the end, then from strs[0] to strs[i-1] (to simulate the loop).
        • Combine all these parts to form a candidate string.
        • Keep track of the lexicographically largest candidate seen so far.
  • Step 3: Return the Result
    • After checking all possibilities, return the largest string found.

This approach ensures we examine all valid combinations efficiently without unnecessary repetition.

Example Walkthrough

Let's take strs = ["abc", "xyz"].

  1. Preprocessing:
    • "abc" vs "cba" → "cba" is larger, so choose "cba"
    • "xyz" vs "zyx" → "zyx" is larger, so choose "zyx"
    • After preprocessing: ["cba", "zyx"]
  2. Try all splits:
    • For each string (cba and zyx), try both orientations (original and reversed), and for each, try splitting at every position (0, 1, 2).
    • For example, using "cba" as original, splitting at position 0: "cba" + "zyx" = "cbazyx"
    • Splitting at position 1: "bac" + "zyx" = "baczyx"
    • Splitting at position 2: "acb" + "zyx" = "acbzyx"
    • Now try "cba" reversed ("abc"), and repeat splits.
    • Repeat the same process for "zyx" (and its reverse "xyz").
    • Keep track of the lex greatest string at each step.
  3. Result:
    • After all splits, the lexicographically largest is "zyxcba".

Time and Space Complexity

  • Brute-force Approach:
    • Would check all 2^n reversal combinations and all split positions, leading to exponential time.
  • Optimized Approach:
    • We only try 2 orientations per string (original and reversed), and for each, we try splitting at every possible character position.
    • If m is the total number of characters across all strings, and n is the number of strings, the total number of splits to check is O(m * 2), since we try both orientations for each position in each string.
    • For each split, we build a string of length O(m), so the total time complexity is O(m^2).
    • Space complexity is O(m) for storing the answer and temporary strings.

Summary

The key to solving the Split Concatenated Strings problem efficiently is to recognize that, for each string, only its original and reversed forms matter. By always picking the lexicographically largest form for each string and systematically trying every possible split point and orientation, we can find the optimal solution without brute-forcing every combination. This approach is both elegant and efficient, leveraging careful preprocessing and smart enumeration of possibilities.