I'm trying to learn the basics of Go and started off by converting old exercises written for Codility in Python over. The code below had a worst-case execution speed of about a quarter second for large strings. When I converted it to Go however, it failed the Codility performance tests for large strings and executed in over 6 seconds.
def solution(S):
stack = []
for i in S:
if len(stack) and stack[-1] == "(" and i == ")":
stack.pop()
continue
stack.append(i)
return 1 if len(stack) == 0 else 0
Go implementation
package solution
func Solution(S string) int {
stack := make([]string, 0)
for i := range S {
s := string([]rune(S)[i])
ln := len(stack)
if ln > 0 && stack[ln-1] == "(" && s == ")" {
stack = stack[:ln-1]
continue
}
stack = append(stack, s)
}
if len(stack) == 0 {
return 1
} else {
return 0
}
}
Can anyone share some insight on how I can properly implement this in Go?
This is the question I'm trying to answer https://codility.com/programmers/lessons/7-stacks_and_queues/nesting/
Working directly with the []byte
will improve your performance drastically.
func Solution(S string) int {
b := []byte(S)
stack := make([]byte, 0)
for i, s := range b {
ln := len(stack)
if ln > 0 && stack[ln-1] == '(' && s == ')' {
stack = stack[:ln-1]
continue
}
stack = append(stack, s)
}
if len(stack) == 0 {
return 1
} else {
return 0
}
}
As mentioned in Yandry Pozo
's answer.
You can make it faster by dropping the append to stack and use a counter instead.
You can iterate over a string using range, keep in mind that you'll get a rune. You can save time avoiding append()
using just a simple counter. Other consideration your algorithm can be even faster if you return early when you find a ')' and the stack is empty:
func Solution(S string) int {
stack := make([]rune, len(S)+1)
top := 0 // keep track of the stack top
for _, r := range S {
if r == '(' { // i got a '('
stack[top] = r
top++
} else { // i got a ')'
top--
if top < 0 { // the stack is emtpy, early return
return 0
}
}
}
if top == 0 {
return 1
}
return 0
}
The slow part of the code is this line:
s := string([]rune(S)[i])
To iterate over the bytes of a string:
for i, b := range []byte(s) {}
To iterate over the code points of a string:
for i, c := range s {}
So just use the two-variable for loop.