↓Skip to main content
  1. LeetCode/

LeetCode 2953: Count Complete Substrings

·3 mins· ·
LeetCode Hard Sliding-Window Hash-Table String
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: hard First Attempt: 2026-01-04

  • Submissions: Wrong Answer ×3 → Accepted

Approach
#

Handle the two conditions separately:

  1. Neighbors differ by <= 2: wherever a letter differs from the previous one by > 2, no complete substring can cross. So split into segments there and count each separately.
  2. Every letter exactly k times: with d distinct letters the length must be d × k. d is only 1 to 26, so run one fixed-length d × k sliding window per d.

The window tracks good, the number of letters appearing exactly k times. Length d × k with good == d means those d letters appear k times each and nothing else is present.

good changes only when a count crosses k: reaching k on add is +1, reaching k + 1 is -1, and the reverse on removal.

My Attempts
#

My approach scanned the whole string for each window size k, 2k, ..., 26k, resetting the window at positions where the difference was > 2.

VersionProblem
Attempt 1The shrink condition was right - left > win_size and there was no check that the length equals win_size, so wrong-length windows were counted. For word = "bd", k = 1, the answer is 3 (b, d, bd); it returned 4
Attempt 2Added the length check, but at a break it reset the window and continued without adding the break character to the new window. For word = "ah", k = 1, the answer is 2; it returned 1
Attempt 3Added the break character, but still continued past the check for the length-1 window; the same example still returned 1
Attempt 4, ACReordered the steps and added a win_size > 1 condition; it passed in 5.5 seconds

Each window rechecked every count with for c in count.values(), which is why it was slow. Splitting first and maintaining a good counter makes each move O(1).

Solution
#

class Solution:
    def countCompleteSubstrings(self, word: str, k: int) -> int:
        def count_segment(seg: str) -> int:
            res = 0
            for d in range(1, 27):                 # d distinct letters in the substring
                size = d * k
                if size > len(seg):
                    break
                cnt = [0] * 26
                good = 0                           # letters appearing exactly k times
                for right, ch in enumerate(seg):
                    c = ord(ch) - 97
                    cnt[c] += 1
                    if cnt[c] == k:
                        good += 1
                    elif cnt[c] == k + 1:
                        good -= 1
                    if right >= size:
                        c = ord(seg[right - size]) - 97
                        if cnt[c] == k:
                            good -= 1
                        elif cnt[c] == k + 1:
                            good += 1
                        cnt[c] -= 1
                    if right >= size - 1 and good == d:
                        res += 1
            return res

        total = start = 0
        for i in range(1, len(word) + 1):
            if i == len(word) or abs(ord(word[i]) - ord(word[i - 1])) > 2:
                total += count_segment(word[start:i])
                start = i
        return total

Complexity
#

Time O(26 · n), space O(26) excluding the sliced segments.

Mistakes to Avoid
#

  • Not fixing the window length: “every letter exactly k times” is not monotonic, so fix the number of distinct letters first.
  • Break handling: splitting into segments first is cleanest, rather than special-casing inside the window.
  • Rechecking every letter’s count per window: a large constant factor.

Interview Explanation
#

Substrings cannot cross a place where neighbors differ by more than 2, so I split the string into segments. Every letter appearing exactly k times means the length is the number of distinct letters times k, so per segment I try d from 1 to 26 with a fixed window of d × k, tracking how many letters appear exactly k times; when that equals d the substring is complete. O(26n) overall.

Related

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 2156: Find Substring With Given Hash Value
·3 mins
LeetCode Hard Sliding-Window Rolling-Hash String
LeetCode 2156 note: the first character has weight p^0, so sliding right to left needs only multiply by p, add the new character, and subtract the outgoing one times p^k; the modulus may not be prime, so no modular inverse
LeetCode 3694: Distinct Points Reachable After Substring Removal
·2 mins
LeetCode Medium Sliding-Window Prefix-Sum Hash-Table String
LeetCode 3694 note: the endpoint is the total displacement minus the removed segment’s displacement; a length-k window tracks that segment and each endpoint goes into a set
LeetCode 3518: Smallest Palindromic Rearrangement II
·3 mins
LeetCode Hard Math Combinatorics Counting Hash-Table String
LeetCode 3518 note: a palindrome is determined by its left half, so this is the k-th permutation of a multiset; try characters position by position and use permutation counts, updated in O(1), to find k’s branch
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 424: Longest Repeating Character Replacement
·2 mins
LeetCode Medium Hash-Table String Sliding-Window
LeetCode 424 note: a variable sliding window where window length minus the top character count is the number of replacements; shrink when it exceeds k