Post

Strings: Longest Substring Without Repeating Characters — Kotlin Solution

Strings: Longest Substring Without Repeating Characters — Kotlin Solution

Problem Info

  
LeetCode #3 — Longest Substring Without Repeating Characters
DifficultyMedium
TopicStrings, Sliding Window, HashSet

Given a string s, find the length of the longest substring without repeating characters.

Example:

1
2
3
4
5
6
7
8
Input:  s = "abcabcbb"
Output: 3   // "abc"

Input:  s = "bbbbb"
Output: 1   // "b"

Input:  s = "pwwkew"
Output: 3   // "wke"

Approach

This is a classic sliding window problem.

Maintain a window with two pointers — left and right. Expand right to grow the window. When a duplicate character is found, shrink from left until the duplicate is gone.

Walk through "abcabcbb":

1
2
3
4
5
6
7
8
9
10
left=0, right=0: window="a"      set={a}       max=1
left=0, right=1: window="ab"     set={a,b}     max=2
left=0, right=2: window="abc"    set={a,b,c}   max=3
left=0, right=3: 'a' duplicate!
  → remove s[left]='a', left=1   set={b,c}
left=1, right=3: window="bca"    set={b,c,a}   max=3
left=1, right=4: 'b' duplicate!
  → remove s[left]='b', left=2   set={c,a}
left=2, right=4: window="cab"    set={c,a,b}   max=3
...and so on

Answer: 3


Kotlin Solution

Approach 1 — Sliding Window with HashSet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun lengthOfLongestSubstring(s: String): Int {
    val seen = HashSet<Char>()
    var left = 0
    var maxLength = 0

    for (right in s.indices) {
        while (s[right] in seen) {
            seen.remove(s[left])
            left++
        }
        seen.add(s[right])
        maxLength = maxOf(maxLength, right - left + 1)
    }

    return maxLength
}

Approach 2 — Sliding Window with HashMap (jump left pointer directly)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun lengthOfLongestSubstring(s: String): Int {
    val lastSeen = HashMap<Char, Int>() // char -> last index
    var left = 0
    var maxLength = 0

    for (right in s.indices) {
        if (s[right] in lastSeen && lastSeen[s[right]]!! >= left) {
            left = lastSeen[s[right]]!! + 1
        }
        lastSeen[s[right]] = right
        maxLength = maxOf(maxLength, right - left + 1)
    }

    return maxLength
}

Why Kotlin Shines Here

s.indices — clean range iteration over string indices:

1
2
for (right in s.indices)
// vs Java: for (int right = 0; right < s.length(); right++)

in operator for HashSet lookup:

1
2
while (s[right] in seen)
// vs Java: while (seen.contains(s.charAt(right)))

!! for known non-null — when you’ve already checked in lastSeen, you know it’s not null:

1
2
lastSeen[s[right]]!! >= left
// The !! is intentional here — we just confirmed the key exists

Two Approaches Compared

ApproachTimeSpaceNotes
HashSetO(n)O(min(n,m))Simple, easy to reason about
HashMapO(n)O(min(n,m))Fewer steps — jumps left directly

m = size of the character set (26 for lowercase letters, 128 for ASCII).

The HashMap approach is more efficient in practice because left jumps directly to the right position instead of moving one step at a time.


Common Mistake

1
2
3
4
5
6
7
8
9
// ❌ Wrong — doesn't handle when duplicate is outside the current window
if (s[right] in lastSeen) {
    left = lastSeen[s[right]]!! + 1  // left might jump backward!
}

// ✅ Correct — only update left if the duplicate is inside the window
if (s[right] in lastSeen && lastSeen[s[right]]!! >= left) {
    left = lastSeen[s[right]]!! + 1
}

Without the >= left check, left could jump to an old position before the current window, giving a wrong (larger) result.


Complexity

  
TimeO(n) — each character visited at most twice
SpaceO(min(n, m)) — window size bounded by character set

Key Takeaway

Sliding window: expand right freely, shrink left only when a duplicate appears. Track what’s in the window with a HashSet or HashMap. The HashMap variant lets you jump left directly instead of crawling.

🔗 Solve it on LeetCode →


📚 Kotlin DSA Series

This post is part of the Kotlin DSA series — solving LeetCode problems using idiomatic Kotlin.

← View Full Series Index
This post is licensed under CC BY 4.0 by the author.