programming exercise
I heard from a friend about an interesting interview question that I thought would be easy.
The problem
the goal of this program is to find the shortest number of stickers used to make a another word given that you have thousands of stickers
for instance, with the word “WPENGINE” printed on them to make for example “WWW” would take 3 and another example is “WINE” would take 1.
The programs logic is really quite literal and was enjoyable to write.
We have a Sticker type who keeps track of the number of letters used and a pile of stickers which is just a list of stickers. We search the pile for a given letter, and if its not found we add another sticker to the pile.
We keep a dummy sticker object around to validate that we do infact have the letters, and error if not.
The code
package main
import (
"fmt"
"os"
"strings"
)
func NewSticker(word string) *Sticker {
s := &Sticker{
letters: make(map[rune]int, 0),
}
var v int
for _, w := range strings.ToLower(word) {
v, _ = s.letters[w]
v += 1
s.letters[w] = v
}
return s
}
type Sticker struct {
letters map[rune]int
}
func (s *Sticker) HasLetter(l rune) bool {
count, found := s.letters[l]
if found == false {
return false
}
if count > 0 {
return true
}
return false
}
func (s *Sticker) Desc() string {
a := make([]string, 0)
for l, cnt := range s.letters {
a = append(a, fmt.Sprintf("%s=%d", string(l), cnt))
}
return strings.Join(a, " ")
}
func (s *Sticker) UseLetter(l rune) error {
fmt.Printf("using letter %s\n", string(l))
count, found := s.letters[l]
if found == false {
return fmt.Errorf("sticker doesn't have letter %s", l)
}
if count == 0 {
return fmt.Errorf("letter is used up")
}
count--
s.letters[l] = count
return nil
}
type Pile struct {
word string
stickers []*Sticker
}
func NewPile(word string) *Pile {
p := &Pile{
word: word,
stickers: make([]*Sticker, 0),
}
return p
}
func (p *Pile) HasLetter(l rune) bool {
for _, sticker := range p.stickers {
if sticker.HasLetter(l) {
return true
}
}
return false
}
func (p *Pile) AddSticker() {
p.stickers = append(p.stickers, NewSticker(p.word))
}
func main() {
var err error
sticker_word := "WPENGINE"
word := "pingping"
if len(os.Args) > 1 {
word = os.Args[1]
}
fmt.Printf("sticker word %s\n", sticker_word)
fmt.Printf("making word %s\n", word)
pile := NewPile(sticker_word)
// just to validate we have the letters..
sticker := NewSticker(sticker_word)
for _, l := range word {
//s := string(l)
fmt.Printf("letter %s\n", string(l))
if sticker.HasLetter(l) == false {
err = fmt.Errorf("no letter %s", string(l))
break
}
if pile.HasLetter(l) == false {
pile.AddSticker()
}
// find a sticker that has the letter we're lookin for in the pile
for _, sticker := range pile.stickers {
if sticker.HasLetter(l) {
sticker.UseLetter(l)
continue
}
}
}
if err != nil {
fmt.Printf("ERROR: %s\n", err)
return
}
// print the results
used := len(pile.stickers)
fmt.Printf("stickers used %d\n", used)
for i, sticker := range pile.stickers {
fmt.Printf("sticker #%d letters %+v\n", i+1, sticker.Desc())
}
}