DEV Community

Cover image for LeetCode Number of Substrings Containing All Three Characters: Sliding Window in Golang
Ehab Hosam
Ehab Hosam

Posted on

LeetCode Number of Substrings Containing All Three Characters: Sliding Window in Golang

Today's LeetCode daily challenge was so fun to solve: Number of
Substrings Containing All Three Characters.

What are we trying to do?

We have a string s that's build using only 3 characters: a, b and c. We wanna know how many substrings in it that has the a, b and c at the same time.

Once you read the description of the problem, and if you know the Sliding window technique, you'll immediately know that this problem has to be solved using a sliding window. It's of course normal to not get the efficient solution if it's your first time to hear about that technique.

A sliding window ... Let's break it down.

It's implemented with 2 pointers: high & low. There is always a condition that you want to achieve, which is to contain all the 3 characters at our problem. To track if we are meeting the condition, we maintain a map that counts how many times did each letter occur in the current substring. We also need a counter variable to increment when we meet our condition.

Both pointers start at first letter.
Image description

We move the high pointer to the right until we meet the condition.

Image description

Met the condition? Increment the counter, but how much should we increment it? In the example shown in the image, the substring "abc" meets our condition, where the 3 letters exist in it. Do we increment by 1? of course no. Wherever the high pointer goes, it will still meet the condition, as you're just adding more letters to the string, so it will always have the "abc" followed by any sequence of letters. So here the "abc", "abca", "abcab" and "abcabc" (4 strings) meet our condition. How to reach that 4? Easy. Subtract the position of the high pointer from the length of the whole string. result += length(string) - high

After incrementing the result, start moving the Low pointer to the right, which cuts from the start of our substring. If you still meet the condition, increment the result again and do so until the condition isn't met, then you start moving the high again.

Image description

Continue this process until the high pointer reaches the last letter of the string, and then we'll have the number we're looking for stored in the result variable.

Implementation

We make a custom map that has the methods we are going to use. The map has 3 properties a, b and c, and it has 3 methods:

  • NewMap: aka constructor.
  • Push: adds a letter (increments its counter)
  • Pop: removes a letter (decrements its counter)
  • IsValid: checks if the current state of the map meets our condition (checks if the 3 letters are present in the current substring).

Code

package main

import (
    "fmt"
)

type Map struct {
    a, b, c int
}

func NewMap() *Map {
    return &Map{}
}

func (m *Map) Push(key byte) {
    switch key {
    case 'a':
        m.a++
    case 'b':
        m.b++
    case 'c':
        m.c++
    }
}

func (m *Map) Pop(key byte) {
    switch key {
    case 'a':
        m.a--
    case 'b':
        m.b--
    case 'c':
        m.c--
    }
}

func (m *Map) IsValid() bool {
    return m.a > 0 && m.b > 0 && m.c > 0
}

func numberOfSubstrings(s string) int {
    occurrences := NewMap()
    result := 0
    low := 0

    for high := range s {
        occurrences.Push(s[high])
        for occurrences.IsValid() {
            result += len(s) - high
            occurrences.Pop(s[low])
            low++
        }
    }

    return result
}



func main() {
    case1 := numberOfSubstrings("abcabc")
    case2 := numberOfSubstrings("aaacb")
    case3 := numberOfSubstrings("abc")
    fmt.Println(case1)
    fmt.Println(case2)
    fmt.Println(case3)
}

Enter fullscreen mode Exit fullscreen mode

See you soon in another challenge :)

Top comments (0)