Problem Description
Given a string s, a substring is called nice if every letter in the substring appears in both uppercase and lowercase. The task is to return the longest nice substring. If there are multiple answers, return the one that occurs earliest in s. If no such substring exists, return an empty string.
Key Insights
- A substring is considered nice if for every character, both its uppercase and lowercase forms are present.
- To check if a substring is nice, using a set to quickly verify the presence of both cases is effective.
- Due to the small constraint (s.length <= 100), an O(n^3) brute-force solution is acceptable, but an efficient divide-and-conquer method can also be applied.
- The divide-and-conquer technique works by identifying an offending character (one missing its counterpart) and then recursively solving for the segments on both sides.
- Keeping track of the earliest occurrence for substrings of the same length is necessary when multiple valid candidates exist.
Space and Time Complexity
Time Complexity: O(n^2) to O(n^3) in the worst-case scenario if using brute force; however, the divide and conquer approach typically performs faster in practice. Space Complexity: O(n) due to recursion and auxiliary data structures (sets).
Solution
We can solve the problem using a recursive divide-and-conquer approach:
- Check if the current string is nice. We do this by creating a set of characters in the string and confirming for each character that its opposite case also exists.
- If the string is nice, return it as a candidate.
- If not, find the first offending character that violates the niceness condition.
- Recursively call the function for the two substrings split at the offending character, and return the longer of the two valid nice substrings.
- This approach ensures that we consider every possibility while naturally skipping segments that cannot form a nice substring.