We use cookies (including Google cookies) to personalize ads and analyze traffic. By continuing to use our site, you accept our Privacy Policy.

Find the Lexicographically Largest String From the Box II

Number: 3749

Difficulty: Hard

Paid? Yes

Companies: N/A


Problem Description

Given a string word and an integer numFriends, we need to simulate a game in which, in every round, word is divided into exactly numFriends non‐empty parts (with the restriction that no split may be repeated in a later round). All parts (from all rounds) are collected into a box. The goal is to determine the lexicographically largest string present in the box.


Key Insights

  • In any valid split of word into numFriends parts, every part is a contiguous substring.
  • For any candidate substring word[L:R+1] to be one of the parts it must be possible to partition the prefix (word[0:L]) into some number of segments (possibly empty for k=1, but actually “non‐empty” so at least L characters must provide k–1 segments) and the suffix (word[R+1:]) into the remaining segments.
  • When the candidate substring is chosen to be the kth segment (1-indexed) in the split, the prefix must have at least k–1 characters (one per segment) and the suffix must have at least numFriends–k characters.
  • For a fixed starting index L the maximum extension R (largest ending index) that can form a valid segment is:
    • If L+1 < numFriends, then R_max = n – (numFriends – (L+1));
    • Otherwise (when L+1 ≥ numFriends), R_max can be extended through the end of word (R_max = n–1).
  • Notice that if two valid candidate segments share the same starting index then the longer one is lexicographically larger (since one string is a prefix of the other and the longer is considered larger).
  • Thus the lexicographically largest valid segment that can start at a given index L is word[L : R_max+1]. Finally, the answer is the maximum over all L.

Space and Time Complexity

Time Complexity: O(n²) in the worst‐case scenario if using naïve substring comparisons – however, using more advanced techniques (such as rolling hashes with binary search for LCP) the comparisons can be optimized to nearly O(n log n) overall. Space Complexity: O(n) for storing auxiliary arrays (if using rolling hash) or O(1) using built–in substring comparisons.


Solution

The idea is to “simulate” every possibility for a segment chosen to come from some valid split. For each starting index L in word, we determine the maximum valid ending index R_max so that the segment word[L : R_max+1] can appear as one of the numFriends parts. This maximum depends on whether there are enough characters before L (to form the segments preceding it) and after R_max (to form the segments following it). In particular:

  • If L+1 < numFriends then there are not enough characters before L to allow arbitrary extension and we must leave enough characters after the candidate segment, i.e. R_max = n – (numFriends – (L+1)).
  • Otherwise, when L+1 ≥ numFriends, the candidate segment can extend all the way to the end (R_max = n–1).

Then we simply choose candidate = word[L : R_max+1] for each L and update our overall best answer if candidate is lexicographically larger. (A slight caveat in a production–level solution is that built–in substring comparisons may be too slow in worst–case scenarios, so one might use rolling–hash based substring comparisons, but for clarity we use simple comparisons here.)

The data structures used are simple index variables and substrings (or slices). The algorithmic approach is an iteration over starting indices combined with a greedy selection of the longest valid candidate from that starting index.


Code Solutions

# Python solution with line-by-line comments

def findLargestString(word: str, numFriends: int) -> str:
    n = len(word)
    best_candidate = ""  # will store the best (lex largest) candidate found so far
    
    # Iterate over each possible starting index L of a candidate segment
    for L in range(n):
        # Determine the maximum valid ending index R_max so that the segment word[L:R_max+1] can be chosen.
        # If L+1 < numFriends then we need to reserve (numFriends - (L+1)) characters for the segments after.
        if L + 1 < numFriends:
            R_max = n - (numFriends - (L + 1))
        else:
            # When there are enough characters available to choose this segment as later part of the partition,
            # we can extend the segment to the end.
            R_max = n - 1
        
        # Only consider if the valid candidate is non-empty
        if R_max >= L:
            candidate = word[L:R_max+1]
            # Update the best_candidate if the current candidate is lexicographically larger.
            if candidate > best_candidate:
                best_candidate = candidate
                
    return best_candidate

# Example usage:
print(findLargestString("dbca", 2))  # Expected output: "dbc"
print(findLargestString("gggg", 4))  # Expected output: "g"
← Back to All Questions