↓Skip to main content
  1. LeetCode/

LeetCode 1763: Longest Nice Substring

·2 mins· ·
LeetCode Easy Sliding-Window Divide-and-Conquer Hash-Table String Bit-Manipulation
Wei Yi Chung
Author
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
Table of Contents

Meta Data
#

Difficulty: easy First Attempt: 2026-01-17

  • Submissions: Wrong Answer ×3 → Accepted (sliding window) → Accepted (divide and conquer)

Approach
#

The same family as LeetCode 395: “both cases of every letter appear” is not monotonic.

Divide and conquer: if a character’s other case appears nowhere in the string, the answer cannot contain it. Split on it, recurse on both sides, and take the longer result, the left one on a tie (the problem wants the earliest).

With n <= 100, enumerating every substring in O(n^2 · 26) also works.

My Attempts
#

My first four versions used 395’s approach: enumerate the number of letters in the window (ignoring case), slide with that fixed, and check whether the uppercase and lowercase letter sets match. All three WAs were in the update when the left character leaves:

VersionProblem
Attempt 1Decremented the distinct count as soon as one case’s count hit zero, though the other case might still be in the window. For s = "AabAAa" the answer is "AAa"; it returned "Aa"
Attempt 2Decremented only when both cases hit zero, but when one hit zero while the other remained, the key was not deleted, so comparing keys() included zero-count keys. For s = "acAbB", "AbB" has no lowercase a but was judged nice; the answer is "bB"
Attempt 3Compared whole dicts, which also required the uppercase and lowercase counts to match. For s = "AaA" the answer is "AaA"; it returned "Aa"
Attempt 4, ACDeleted keys at zero and decremented the distinct count only when both cases were gone

Switching to divide and conquer afterwards was much simpler.

Solution
#

class Solution:
    def longestNiceSubstring(self, s: str) -> str:
        chars = set(s)
        for i, c in enumerate(s):
            if c.swapcase() not in chars:          # c cannot be in the answer
                left = self.longestNiceSubstring(s[:i])
                right = self.longestNiceSubstring(s[i + 1:])
                return left if len(left) >= len(right) else right   # left wins ties
        return s

Complexity
#

Time O(n^2) (one split per level, at most n levels of O(n)), space O(n) recursion depth.

Mistakes to Avoid
#

  • On equal lengths, return the earliest substring.
  • When comparing dict.keys(), delete keys whose count reaches zero.

Interview Explanation
#

A character whose other case never appears cannot be in the answer, so I split the string on it, recurse on both sides, and take the longer result, the left one on a tie. When every character has its pair, the whole string is the answer. With n only 100, the worst case O(n^2) is fine.

Related

LeetCode 395: Longest Substring with At Least K Repeating Characters
·3 mins
LeetCode Medium Sliding-Window Divide-and-Conquer Hash-Table String
LeetCode 395 note: “every character at least k times” is not monotonic, so a direct slide fails; divide and conquer splits on characters with fewer than k occurrences, or fixing the number of distinct characters makes the window slidable
LeetCode 1016: Binary String With Substrings Representing 1 To N
·3 mins
LeetCode Medium Sliding-Window String Bit-Manipulation Hash-Table
LeetCode 1016 note: s has at most 1000 characters, so there are few substrings of any fixed length and a large n must be False; checking from n downward fails fast, or extend up to 30 bits from each start to collect every value <= n
LeetCode 187: Repeated DNA Sequences
·1 min
LeetCode Medium Hash-Table String Bit-Manipulation Sliding-Window
LeetCode 187 note: count every length-10 substring in a hash map and return those seen more than once
LeetCode 389: Find the Difference
·1 min
LeetCode Easy Hash-Table String Bit-Manipulation Sorting
LeetCode 389 note: t has one extra character; XOR every character of both strings, pairs cancel, and the extra one remains
LeetCode 3090: Maximum Length Substring With Two Occurrences
·1 min
LeetCode Easy Sliding-Window Hash-Table String
LeetCode 3090 note: a longest-window slide that shrinks the left whenever the newly added character appears more than twice
LeetCode 2781: Length of the Longest Valid Substring
·3 mins
LeetCode Hard Sliding-Window Hash-Table String Trie
LeetCode 2781 note: forbidden words are at most 10 long, so each right end checks only the last 10 suffixes; on a match push the left end past it, using the shortest match (the rightmost start)