diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1d74e21 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.vscode/ diff --git a/binary-tree-maximum-path-sum.go b/binary-tree-maximum-path-sum.go new file mode 100644 index 0000000..2d549fc --- /dev/null +++ b/binary-tree-maximum-path-sum.go @@ -0,0 +1,48 @@ +package main + +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +func max2(a, b int) int { + if a > b { + return a + } + + return b +} + +func max3(a, b, c int) int { + return max2(a, max2(b, c)) +} + +func traverse(root *TreeNode, maxSum *int) int { + if root == nil { + return 0 + } + + lPathMaxSum := traverse(root.Left, maxSum) + rPathMaxSum := traverse(root.Right, maxSum) + rootPathMaxSum := max3(root.Val, root.Val+lPathMaxSum, root.Val+rPathMaxSum) + + *maxSum = max3(*maxSum, root.Val+lPathMaxSum+rPathMaxSum, rootPathMaxSum) + + return rootPathMaxSum +} + +func maxPathSum(root *TreeNode) int { + if root == nil { + return 0 + } + + maxSum := root.Val + traverse(root, &maxSum) + + return maxSum +} + +func main() { + +} diff --git a/combination-sum-2.go b/combination-sum-2.go new file mode 100644 index 0000000..338b3c4 --- /dev/null +++ b/combination-sum-2.go @@ -0,0 +1,62 @@ +package main + +import ( + "fmt" + "sort" +) + +type Ints []int + +func (ints Ints) Len() int { + return len(ints) +} + +func (ints Ints) Less(i, j int) bool { + return ints[i] < ints[j] +} + +func (ints Ints) Swap(i, j int) { + ints[i], ints[j] = ints[j], ints[i] +} + +func findCombinations(candidates []int, target int, selectedCandidates []int, results [][]int) [][]int { + if target < 0 { + return results + } + + if target == 0 { + sel := make([]int, len(selectedCandidates)) + copy(sel, selectedCandidates) + results = append(results, sel) + + return results + } + + for i := 0; i < len(candidates); i++ { + if i > 0 && candidates[i] == candidates[i-1] { + continue + } + + selectedCandidates = append(selectedCandidates, candidates[i]) + results = findCombinations(candidates[i+1:], target-candidates[i], selectedCandidates, results) + selectedCandidates = selectedCandidates[:len(selectedCandidates)-1] + } + + return results +} + +func combinationSum2(candidates []int, target int) [][]int { + sort.Sort(Ints(candidates)) + + return findCombinations(candidates, target, []int{}, [][]int{}) +} + +func tests() { + fmt.Printf("%v\n", combinationSum2([]int{10, 1, 2, 7, 6, 1, 5}, 8)) + fmt.Printf("%v\n", combinationSum2([]int{2, 5, 2, 1, 2}, 5)) + fmt.Printf("%v\n", combinationSum2([]int{1, 1, 2, 2}, 5)) +} + +func main() { + tests() +} diff --git a/construct-binary-tree-from-preorder-and-inorder-traversal.go b/construct-binary-tree-from-preorder-and-inorder-traversal.go new file mode 100644 index 0000000..9db9650 --- /dev/null +++ b/construct-binary-tree-from-preorder-and-inorder-traversal.go @@ -0,0 +1,85 @@ +package main + +import "fmt" + +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ + +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +func getSubTree(preorder []int, preorderStart, preorderEnd int, inorder []int, inorderStart, inorderEnd int, preorderOffsetLUT, inorderOffsetLUT map[int]int) *TreeNode { + if len(preorder) == 0 || len(inorder) == 0 || preorderStart > preorderEnd || inorderStart > inorderEnd { + return nil + } + + root := preorder[preorderStart] + rootInorderOffset := inorderOffsetLUT[root] + lSubTreeLen := rootInorderOffset - inorderStart + + if lSubTreeLen < 0 { + fmt.Printf("****ERROR******\n") + fmt.Printf("root: %d; preorderStart: %d; inorderStart: %d; rootInorderOffset: %d; lSubTreeLen: %d\n", + root, preorderStart, inorderStart, rootInorderOffset, lSubTreeLen) + + return nil + } + + // fmt.Printf("getting lSubTree\n") + lSubTree := getSubTree(preorder, preorderStart+1, preorderStart+1+lSubTreeLen-1, + inorder, inorderStart, inorderStart+lSubTreeLen-1, preorderOffsetLUT, inorderOffsetLUT) + + // fmt.Printf("getting rSubTree\n") + rSubTree := getSubTree(preorder, preorderStart+lSubTreeLen+1, preorderEnd, + inorder, rootInorderOffset+1, inorderEnd, preorderOffsetLUT, inorderOffsetLUT) + + return &TreeNode{Val: root, Left: lSubTree, Right: rSubTree} +} + +func inorderTraversal(root *TreeNode) { + if root == nil { + return + } + + inorderTraversal(root.Left) + fmt.Printf("%d ", root.Val) + inorderTraversal(root.Right) +} + +func buildTree(preorder []int, inorder []int) *TreeNode { + preorderOffsetLUT := make(map[int]int) + inorderOffsetLUT := make(map[int]int) + + for i := 0; i < len(preorder); i++ { + preorderOffsetLUT[preorder[i]] = i + inorderOffsetLUT[inorder[i]] = i + } + + return getSubTree(preorder, 0, len(preorder)-1, inorder, 0, len(inorder)-1, preorderOffsetLUT, inorderOffsetLUT) +} + +func test1() { + // preorder := []int{3, 9, 20, 15, 7} + // inorder := []int{9, 3, 15, 20, 7} + preorder := []int{5, 1, 9} + inorder := []int{1, 5, 9} + // preorder := []int{1, 5} + // inorder := []int{1, 5} + + tree := buildTree(preorder, inorder) + + inorderTraversal(tree) +} + +func main() { + test1() +} diff --git a/course-schedule-ii.go b/course-schedule-ii.go new file mode 100644 index 0000000..9a443f4 --- /dev/null +++ b/course-schedule-ii.go @@ -0,0 +1,190 @@ +package main + +import "fmt" + +type intSet struct { + size int + elements map[int]struct{} +} + +func NewIntSet() intSet { + return intSet{size: 0, elements: make(map[int]struct{})} +} + +func (s *intSet) Add(elem int) { + if _, exists := s.elements[elem]; !exists { + s.elements[elem] = struct{}{} + s.size++ + } + + return +} + +func (s *intSet) Remove(elem int) { + if _, exists := s.elements[elem]; exists { + delete(s.elements, elem) + s.size-- + } + + return +} + +func (s *intSet) Contains(elem int) bool { + _, exists := s.elements[elem] + + return exists +} + +func (s *intSet) GetAnyElement() (int, error) { + if len(s.elements) == 0 { + return 0, fmt.Errorf("set is empty") + } + + var elem int + for k, _ := range s.elements { + elem = k + + break + } + + return elem, nil +} + +// Return true if cycle exists. +func checkCycleFrom(start int, adjs [][]int, whiteSet, graySet, blackSet intSet) bool { + if blackSet.Contains(start) { + return false + } + + if graySet.Contains(start) { + return true + } + + if whiteSet.Contains(start) { + whiteSet.Remove(start) + graySet.Add(start) + } + + // Explore all adjacencies + for i := 0; i < len(adjs[start]); i++ { + cycleExists := checkCycleFrom(adjs[start][i], adjs, whiteSet, graySet, blackSet) + + if cycleExists { + return true + } + } + + graySet.Remove(start) + blackSet.Add(start) + + return false +} + +func checkCycle(adjs [][]int) bool { + whiteSet, graySet, blackSet := NewIntSet(), NewIntSet(), NewIntSet() + + for i := 0; i < len(adjs); i++ { + whiteSet.Add(i) + } + + for { + elem, err := whiteSet.GetAnyElement() + + if err != nil { + break + } + + if checkCycleFrom(elem, adjs, whiteSet, graySet, blackSet) { + return true + } + } + + return false +} + +func topologicalTraversal(start int, adjs [][]int, visited map[int]struct{}, traversal []int) []int { + if _, exists := visited[start]; exists { + return traversal + } + + for _, to := range adjs[start] { + traversal = topologicalTraversal(to, adjs, visited, traversal) + } + + visited[start] = struct{}{} + traversal = append(traversal, start) + + return traversal +} + +func findOrder(numCourses int, prerequisites [][]int) []int { + adjs := make([][]int, numCourses) + + for i, _ := range adjs { + adjs[i] = []int{} + } + + visited := make(map[int]struct{}) + leafCheck := make([]bool, numCourses) + + for i, _ := range leafCheck { + leafCheck[i] = true + } + + for _, pair := range prerequisites { + from, to := pair[0], pair[1] + + adjs[from] = append(adjs[from], to) + + leafCheck[to] = false + } + + if checkCycle(adjs) { + return []int{} + } + + order := []int{} + + for i := 0; i < numCourses; i++ { + if leafCheck[i] { + // leaf course - this is a course that no other course depends on + order = topologicalTraversal(i, adjs, visited, order) + } + } + + for i := 0; i < numCourses; i++ { + if _, exists := visited[i]; !exists { + return []int{} + } + } + + return order +} + +type testcase struct { + numCourses int + preReqs [][]int +} + +func tests() { + testcases := []testcase{ + testcase{numCourses: 2, preReqs: [][]int{[]int{1, 0}}}, + testcase{numCourses: 4, preReqs: [][]int{[]int{1, 0}, []int{2, 0}, []int{3, 1}, []int{3, 2}}}, + testcase{numCourses: 3, preReqs: [][]int{[]int{0, 2}, []int{1, 2}, []int{2, 0}}}, + } + + for i, tc := range testcases { + order := findOrder(tc.numCourses, tc.preReqs) + + fmt.Printf("Test case: %d; order: %v\n", i, order) + } +} + +func main() { + // tests() + s1 := NewIntSet() + + s1.Add(4) + + fmt.Printf("s1.size = %d\n", s1.size) +} diff --git a/delete-columns-to-make-sorted-ii.go b/delete-columns-to-make-sorted-ii.go new file mode 100644 index 0000000..4042b24 --- /dev/null +++ b/delete-columns-to-make-sorted-ii.go @@ -0,0 +1,102 @@ +package main + +import "fmt" + +func minDeletionSize(A []string) int { + charss := make([][]rune, len(A)) + + for i, _ := range A { + charss[i] = []rune(A[i]) + } + + strictlyGreater := make([]bool, len(A)) + numDeletions := 0 + + for j := 0; j < len(A[0]); j++ { + monotonicIncreasing := true + + for i := 1; i < len(A); i++ { + if charss[i-1][j] >= charss[i][j] { + monotonicIncreasing = false + } + + if charss[i-1][j] > charss[i][j] { + if strictlyGreater[i] { + continue + } + + // inversion + if j == 0 { + // inversion in first column + numDeletions++ + + for k := 0; k < len(A); k++ { + charss[k][j] = '_' + } + + break + } else { + // inversion that's not in first column + if charss[i-1][j-1] == charss[i][j-1] { + // copy previous column and delete this one + numDeletions++ + + for k := 0; k < len(A); k++ { + charss[k][j] = charss[k][j-1] + } + + break + } + } + } + } + + for i := 1; i < len(A); i++ { + if charss[i-1][j] < charss[i][j] { + strictlyGreater[i] = true + } + } + + if monotonicIncreasing { + return numDeletions + } + } + + return numDeletions +} + +func test1() { + A := []string{ + "ca", + "bb", + "ac", + } + + fmt.Printf("minDeletions: %d\n", minDeletionSize(A)) +} + +func test2() { + A := []string{ + "xc", + "yb", + "za", + } + + fmt.Printf("minDeletions: %d\n", minDeletionSize(A)) +} + +func test3() { + A := []string{ + "zyx", + "wvu", + "tsr", + } + + fmt.Printf("minDeletions: %d\n", minDeletionSize(A)) +} + +func main() { + // test1() + // test2() + test3() +} diff --git a/deo-evaporator.go b/deo-evaporator.go new file mode 100644 index 0000000..af8dd52 --- /dev/null +++ b/deo-evaporator.go @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + "math" +) + +func Evaporator(content float64, evapPerDay int, threshold int) int { + // your code + n := math.Log10(float64(threshold) / 100) / math.Log10(1 - float64(evapPerDay) / 100.0) + + return int(math.Ceil(n)) +} + +func main() { + fmt.Printf("%d\n", Evaporator(10, 10, 10)) + fmt.Printf("%d\n", Evaporator(10, 10, 5)) + fmt.Printf("%d\n", Evaporator(100, 5, 5)) +} diff --git a/design-hit-counter.go b/design-hit-counter.go new file mode 100644 index 0000000..2a31181 --- /dev/null +++ b/design-hit-counter.go @@ -0,0 +1,155 @@ +package main + +import ( + "fmt" +) + +const ( + MaxNumCounts = 300 +) + +type HitCounter struct { + head int + vHead int + tail int + vTail int + counts []int +} + +/** Initialize your data structure here. */ +func Constructor() HitCounter { + return HitCounter{ + head: MaxNumCounts - 1, + vHead: MaxNumCounts, + tail: 0, + vTail: 1, + counts: make([]int, MaxNumCounts), + } +} + +func (this *HitCounter) resetCounts() { + for i, _ := range this.counts { + this.counts[i] = 0 + } +} + +func (this *HitCounter) sum() int { + s := 0 + + for _, c := range this.counts { + s += c + } + + return s +} + +/** Record a hit. + @param timestamp - The current timestamp (in seconds granularity). */ +func (this *HitCounter) Hit(timestamp int) { + if timestamp-this.vHead >= MaxNumCounts { + this.resetCounts() + this.head = MaxNumCounts - 1 + this.tail = 0 + this.vHead = timestamp + this.vTail = this.vHead - MaxNumCounts + 1 + this.counts[this.head] += 1 + + return + } + + if this.vHead < timestamp { + for this.vHead < timestamp { + this.vHead++ + this.vTail++ + + this.head = (this.head + 1) % MaxNumCounts + this.tail = (this.tail + 1) % MaxNumCounts + + this.counts[this.head] = 0 + } + + this.counts[this.head] = 1 + + return + } + + // this.vHead >= timestamp + offset2 := (this.tail + (timestamp - this.vTail)) % MaxNumCounts + this.counts[offset2] += 1 +} + +/** Return the number of hits in the past 5 minutes. +@param timestamp - The current timestamp (in seconds granularity). +*/ +func (this *HitCounter) GetHits(timestamp int) int { + if timestamp-this.vHead >= MaxNumCounts { + this.resetCounts() + this.head = MaxNumCounts - 1 + this.tail = 0 + this.vHead = timestamp + this.vTail = this.vHead - MaxNumCounts + 1 + + return 0 + } + + if this.vHead < timestamp { + for this.vHead < timestamp { + this.vHead++ + this.vTail++ + + this.head = (this.head + 1) % MaxNumCounts + this.tail = (this.tail + 1) % MaxNumCounts + + this.counts[this.head] = 0 + } + + this.counts[this.head] = 0 + + return this.sum() + } + + // this.vHead >= timestamp + return this.sum() +} + +/** + * Your HitCounter object will be instantiated and called as such: + * obj := Constructor(); + * obj.Hit(timestamp); + * param_2 := obj.GetHits(timestamp); + */ + +func test1() { + hc := Constructor() + + hc.Hit(1) + hc.Hit(2) + hc.Hit(3) + fmt.Printf("getHits(4) = %d (expected: 3)\n", hc.GetHits(4)) + + hc.Hit(300) + fmt.Printf("getHits(300) = %d (expected: 4)\n", hc.GetHits(300)) + + fmt.Printf("getHits(301) = %d (expected: 3)\n", hc.GetHits(301)) +} + +func test2() { + hc := Constructor() + + hc.Hit(1) + hc.Hit(1) + hc.Hit(1) + hc.Hit(300) + fmt.Printf("getHits(300) = %d (expected: 4)\n", hc.GetHits(300)) + + hc.Hit(300) + fmt.Printf("getHits(300) = %d (expected: 5)\n", hc.GetHits(300)) + + hc.Hit(301) + fmt.Printf("getHits(301) = %d (expected: 3)\n", hc.GetHits(301)) +} + +func main() { + test1() + // test2() +} diff --git a/direction-and-position-rule-verification.go b/direction-and-position-rule-verification.go new file mode 100644 index 0000000..248f929 --- /dev/null +++ b/direction-and-position-rule-verification.go @@ -0,0 +1,80 @@ +package main + +import ( + "fmt" + "strings" +) + +type edge struct { + from string + to string + dirLabel string // read as is of +} + +type vertexCoords struct { + x int + y int +} + +type vertexAdjs struct { + vertex string + fromEdges []edge + toEdges []edge +} + +func processDirections(directions []string) []edge { + edges := []edge{} + + for _, direction := range directions { + dirParts := strings.Split(direction, " ") + from, dirLabel, to := dirParts[0], dirParts[1], dirParts[2] + edges = append(edges, edge{from: from, to: to, dirLabel: dirLabel}) + } + + return edges +} + +func buildVertextAdjs(edges []edge) map(string, []vertexAdjs) { + adjs := make(map(string, vertexAdjs)) + + for _, e := range edges { + f, t := e.from, e.to + + _, okf := adjs[f] + _, okt := adjs[t] + + if !okf { + adjs[f] = vertexAdjs{vertex: f, fromEdges: []edge{}, toEdges: []edge{}} + } + + if !okt { + adjs[t] = vertexAdjs{vertex: f, fromEdges: []edge{}, toEdges: []edge{}} + } + + + } +} + +type testcase struct { + directions []string + expectedValidity bool +} + +func test() { + tcs := []testcase{ + testcase { + directions: []string{"A NE B", "A N B"}, + expectedValidity: true, + }, + } + + for _, tc := range tcs { + edges := processDirections(tc.directions) + + fmt.Printf("edges: %v\n", edges) + } +} + +func main() { + test() +} diff --git a/ds/doubly-linked-list.go b/ds/doubly-linked-list.go new file mode 100644 index 0000000..4c2fddf --- /dev/null +++ b/ds/doubly-linked-list.go @@ -0,0 +1,67 @@ +package ds + +import ( + "errors" +) + +type LinkedNode struct { + value interface{} + next *LinkedNode + prev *LinkedNode +} + +func (l *LinkedNode) IsEmpty() bool { + if l.next == nil && l.prev == nil { + return true + } + + if l.next == l && l.prev == l { + return true + } + + return false +} + +func (l *LinkedNode) AddNode(newNode *LinkedNode) { + if l.IsEmpty() { + l.next = newNode + l.prev = newNode + newNode.prev = l + newNode.next = l + } else { + currFirstNode := l.next + l.next = newNode + newNode.next = currFirstNode + newNode.prev = l + currFirstNode.prev = newNode + } +} + +// Should only be called from the head of the list (the head node is essentially the sentinel) +func (l *LinkedNode) Traverse(nodeVisitorFn func(data interface{})) { + for curr := l.next; curr != l; curr = curr.next { + nodeVisitorFn(curr.value) + } +} + +func DeleteNode(l *LinkedNode) error { + if l.IsEmpty() { + return errors.New("DeleteNode called on an empty list") + } + + next := l.next + prev := l.prev + + next.prev = prev + prev.next = next + + return nil +} + +func NewLinkedNode(data interface{}) *LinkedNode { + return &LinkedNode{value: data} +} + +func NewEmptyLinkedNode() *LinkedNode { + return &LinkedNode{} +} diff --git a/ds/doubly-linked-list_test.go b/ds/doubly-linked-list_test.go new file mode 100644 index 0000000..67c8155 --- /dev/null +++ b/ds/doubly-linked-list_test.go @@ -0,0 +1,60 @@ +package ds + +import ( + "testing" +) + +func TestIsEmpty(t *testing.T) { + list := NewEmptyLinkedNode() + + if !list.IsEmpty() { + t.Fatalf("Expected list to be empty") + } +} + +func TestAddAndTraverse(t *testing.T) { + head := NewEmptyLinkedNode() + + head.AddNode(NewLinkedNode(1)) + head.AddNode(NewLinkedNode(2)) + + visitorFn := func(d interface{}) { + num, ok := d.(int) + + if !ok { + t.Fatalf("Unexpected type") + } + + t.Logf("Data: %d\n", num) + } + + head.Traverse(visitorFn) +} + +func TestDeleteAllNodes(t *testing.T) { + head := NewEmptyLinkedNode() + + node1 := NewLinkedNode(1) + node2 := NewLinkedNode(2) + + head.AddNode(node1) + head.AddNode(node2) + + DeleteNode(node2) + + // visitorFn := func(d interface{}) { + // num, ok := d.(int) + + // if !ok { + // t.Fatalf("Unexpected type") + // } + + // t.Logf("Data: %d\n", num) + // } + + DeleteNode(node1) + + if !head.IsEmpty() { + t.Fatalf("Expected list to be empty but it's not") + } +} diff --git a/edit-distance.go b/edit-distance.go new file mode 100644 index 0000000..238c342 --- /dev/null +++ b/edit-distance.go @@ -0,0 +1,52 @@ +package main + +func min2(a, b int) int { + if a < b { + return a + } + + return b +} + +func min3(a, b, c int) int { + return min2(a, min2(b, c)) +} + +func minDistance(word1 string, word2 string) int { + w1Chars, w2Chars := []rune(word1), []rune(word2) + w1Len, w2Len := len(w1Chars), len(w2Chars) + mat := make([][]int, w1Len+1) + + for i, _ := range mat { + mat[i] = make([]int, w2Len+1) + } + + // mat[i][j]: the minimum number of edits neeed to transform + // word1's i-prefix (prefix of length i) to word2's j-prefix + // (prefix of length j) + for i := 0; i <= w1Len; i++ { + mat[i][0] = i + } + + for j := 0; j <= w2Len; j++ { + mat[0][j] = j + } + + for i := 1; i <= w1Len; i++ { + for j := 1; j <= w2Len; j++ { + if w1Chars[i-1] == w2Chars[j-1] { + mat[i][j] = mat[i-1][j-1] + + continue + } + + mat[i][j] = min3(1+mat[i-1][j-1], 1+mat[i-1][j], 1+mat[i][j-1]) + } + } + + return mat[w1Len][w2Len] +} + +func main() { + +} diff --git a/fair-coin-from-biased.go b/fair-coin-from-biased.go new file mode 100644 index 0000000..c165b3f --- /dev/null +++ b/fair-coin-from-biased.go @@ -0,0 +1,55 @@ +package main + +import ( + "fmt" + "math/rand" +) + +func biasedCoin() int { + n := rand.Intn(100) + + if n < 60 { + return 0 + } + + return 1 +} + +func fairCoin() int { + for { + coin1 := biasedCoin() + coin2 := biasedCoin() + + if coin1 != coin2 { + return coin1 + } + } +} + +type CoinFn func() int + +func flip() { + zeros := 0 + ones := 0 + + var flipCoin CoinFn + + // flipCoin = biasedCoin + flipCoin = fairCoin + + for i := 0; i < 100; i++ { + flipResult := flipCoin() + + if flipResult == 0 { + zeros++ + } else { + ones++ + } + } + + fmt.Printf("zeros: %d; ones: %d\n", zeros, ones) +} + +func main() { + flip() +} diff --git a/find-all-anagrams-in-a-string.go b/find-all-anagrams-in-a-string.go new file mode 100644 index 0000000..8f0e8a5 --- /dev/null +++ b/find-all-anagrams-in-a-string.go @@ -0,0 +1,121 @@ +package main + +import ( + "fmt" +) + +func dumpNonZeroCounts(counts []int) { + for i, count := range counts { + if count == 0 { + continue + } + + fmt.Printf("%c: %d; ", rune(i), count) + } + + fmt.Println() +} + +func findAnagrams(s string, p string) []int { + if len(s) < len(p) { + return []int{} + } + + pChars := []rune(p) + sChars := []rune(s) + pCharCounts := make([]int, 256) + sWindowCharCounts := make([]int, 256) + + for _, pChar := range pChars { + pCharCounts[pChar] += 1 + } + + for i := 0; i < len(pChars); i++ { + sWindowCharCounts[sChars[i]] += 1 + } + + numDiffs := 0 + + for i := 0; i < len(pCharCounts); i++ { + if pCharCounts[i] != sWindowCharCounts[i] { + numDiffs++ + } + } + + start, end := 0, len(pChars)-1 + anagramOffsets := make([]int, 0, len(pChars)) + + for end < len(sChars) { + if numDiffs == 0 { + anagramOffsets = append(anagramOffsets, start) + } + + sWindowCharCounts[sChars[start]]-- + + if sWindowCharCounts[sChars[start]] == pCharCounts[sChars[start]] { + numDiffs-- + } else if sWindowCharCounts[sChars[start]]+1 == pCharCounts[sChars[start]] { + numDiffs++ + } + + start++ + + end++ + + if end >= len(sChars) { + break + } + + sWindowCharCounts[sChars[end]]++ + + if sWindowCharCounts[sChars[end]] == pCharCounts[sChars[end]] { + numDiffs-- + } else if sWindowCharCounts[sChars[end]]-1 == pCharCounts[sChars[end]] { + numDiffs++ + } + + // fmt.Printf("At start=%d: ", start) + // dumpNonZeroCounts(sWindowCharCounts) + } + + return anagramOffsets +} + +type testcase struct { + s string + p string + expectedResult []int +} + +func slicesEqual(a, b []int) bool { + if len(a) != len(b) { + return false + } + + for i, n := range a { + if n != b[i] { + return false + } + } + + return true +} + +func runTests() { + testcases := []testcase{ + testcase{"cbaebabacd", "abc", []int{0, 6}}, + testcase{"abab", "ab", []int{0, 1, 2}}, + } + + for i, tc := range testcases { + result := findAnagrams(tc.s, tc.p) + + if !slicesEqual(result, tc.expectedResult) { + fmt.Printf("Test %d failed; expected: %v; got %v\n", i, tc.expectedResult, result) + } + } +} + +func main() { + runTests() +} diff --git a/find-if-string-is-k-palindrome-or-not.go b/find-if-string-is-k-palindrome-or-not.go new file mode 100644 index 0000000..df729c6 --- /dev/null +++ b/find-if-string-is-k-palindrome-or-not.go @@ -0,0 +1,61 @@ +package main + +import "fmt" + +func min2(a, b int) int { + if a < b { + return a + } + + return b +} + +func min3(a, b, c int) int { + return min2(a, min2(b, c)) +} + +func minDistance(word1 string, word2 string) int { + w1Chars, w2Chars := []rune(word1), []rune(word2) + w1Len, w2Len := len(w1Chars), len(w2Chars) + mat := make([][]int, w1Len+1) + + for i, _ := range mat { + mat[i] = make([]int, w2Len+1) + } + + // mat[i][j]: the minimum number of edits neeed to transform + // word1's i-prefix (prefix of length i) to word2's j-prefix + // (prefix of length j) + for i := 0; i <= w1Len; i++ { + mat[i][0] = i + } + + for j := 0; j <= w2Len; j++ { + mat[0][j] = j + } + + for i := 1; i <= w1Len; i++ { + for j := 1; j <= w2Len; j++ { + if w1Chars[i-1] == w2Chars[j-1] { + mat[i][j] = mat[i-1][j-1] + + continue + } + + mat[i][j] = min3(1+mat[i-1][j-1], 1+mat[i-1][j], 1+mat[i][j-1]) + } + } + + return mat[w1Len][w2Len] +} + +func runTests() { + fmt.Printf("edits: %d\n", minDistance("abbac", "cabba")) + fmt.Printf("edits: %d\n", minDistance("abcdecba", "abcedcba")) + fmt.Printf("edits: %d\n", minDistance("abcdeca", "acedcba")) + +} + +func main() { + runTests() +} diff --git a/implement-queue-using-stacks.go b/implement-queue-using-stacks.go new file mode 100644 index 0000000..93bb828 --- /dev/null +++ b/implement-queue-using-stacks.go @@ -0,0 +1,73 @@ +package main + +import "fmt" + +type MyQueue struct { + pushStack []int + popStack []int +} + +/** Initialize your data structure here. */ +func Constructor() MyQueue { + return MyQueue{ + pushStack: []int{}, + popStack: []int{}, + } +} + +/** Push element x to the back of queue. */ +func (this *MyQueue) Push(x int) { + this.pushStack = append(this.pushStack, x) +} + +/** Removes the element from in front of queue and returns that element. */ +func (this *MyQueue) Pop() int { + if len(this.popStack) == 0 { + this.TransferPushToPopStack() + } + + popped := this.popStack[len(this.popStack)-1] + this.popStack = this.popStack[:len(this.popStack)-1] + + return popped +} + +func (this *MyQueue) TransferPushToPopStack() { + if len(this.pushStack) == 0 { + return + } + + for i := len(this.pushStack) - 1; i >= 0; i-- { + this.popStack = append(this.popStack, this.pushStack[i]) + } + + this.pushStack = this.pushStack[0:0] +} + +/** Get the front element. */ +func (this *MyQueue) Peek() int { + if len(this.popStack) == 0 { + this.TransferPushToPopStack() + } + + return this.popStack[len(this.popStack)-1] +} + +/** Returns whether the queue is empty. */ +func (this *MyQueue) Empty() bool { + return len(this.pushStack) == 0 && len(this.popStack) == 0 +} + +func test() { + obj := Constructor() + obj.Push(5) + obj.Push(2) + param_2 := obj.Pop() + param_4 := obj.Empty() + + fmt.Printf("%v, %v\n", param_2, param_4) +} + +func main() { + test() +} diff --git a/knapsack-0-1.go b/knapsack-0-1.go new file mode 100644 index 0000000..fd8fd93 --- /dev/null +++ b/knapsack-0-1.go @@ -0,0 +1,69 @@ +package main + +import "fmt" + +func max(a, b int) int { + if a > b { + return a + } + + return b +} + +func maxKnapsackValue(w int, ws, vs []int) int { + dp := make([][]int, w+1) + n := len(ws) + + for i, _ := range dp { + dp[i] = make([]int, n+1) + } + + // dp[i][j] is the maximum value of the knapsack containing + // a subset of the items [0..(j - 1)] whose weights add up to + // less than or equal to i + for j := 0; j <= n; j++ { + dp[0][j] = 0 + } + + for i := 1; i <= w; i++ { + dp[i][0] = 0 + + for j := 1; j <= n; j++ { + dp[i][j] = dp[i][j-1] + + if i-ws[j-1] >= 0 { + dp[i][j] = max(dp[i][j], dp[i-ws[j-1]][j-1]+vs[j-1]) + } + } + } + + return dp[w][n] +} + +type testcase struct { + weights []int + values []int + w int + expectedMaxValue int +} + +func test() { + tcs := []testcase{ + testcase{weights: []int{3, 2, 4, 1}, values: []int{100, 20, 60, 40}, w: 5, expectedMaxValue: 140}, + testcase{weights: []int{10, 20, 30}, values: []int{60, 100, 120}, w: 50, expectedMaxValue: 220}, + } + + for i, tc := range tcs { + result := maxKnapsackValue(tc.w, tc.weights, tc.values) + + if result != tc.expectedMaxValue { + fmt.Printf("Test case %d FAILED (expected: %d, actual: %d)\n", i, tc.expectedMaxValue, result) + } else { + fmt.Printf("Test case %d PASSED\n", i) + } + } +} + +func main() { + test() +} diff --git a/largest-bst-subtree.go b/largest-bst-subtree.go new file mode 100644 index 0000000..f9c5724 --- /dev/null +++ b/largest-bst-subtree.go @@ -0,0 +1,95 @@ +package main + +import ( + "fmt" +) + +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +func max2(a, b int) int { + if a > b { + return a + } + + return b +} + +func min2(a, b int) int { + if a < b { + return a + } + + return b +} + +func bstSum(root *TreeNode, maxBSTSize *int) (bool, int, int, int) { + if root.Left == nil && root.Right == nil { + *maxBSTSize = max2(*maxBSTSize, 1) + + return true, 1, root.Val, root.Val + } + + isTreeBST := false + treeMin, treeMax := 0, 0 + treeSize := 0 + + if root.Left != nil && root.Right != nil { + isLSubTreeBST, lSubTreeSize, lSubTreeMin, lSubTreeMax := bstSum(root.Left, maxBSTSize) + isRSubTreeBST, rSubTreeSize, rSubTreeMin, rSubTreeMax := bstSum(root.Right, maxBSTSize) + + isTreeBST = isLSubTreeBST && isRSubTreeBST && (root.Val > lSubTreeMax) && (root.Val < rSubTreeMin) + treeSize = 1 + lSubTreeSize + rSubTreeSize + treeMin, treeMax = min2(root.Val, min2(lSubTreeMin, rSubTreeMin)), max2(root.Val, max2(lSubTreeMax, rSubTreeMax)) + } else if root.Left == nil { + isRSubTreeBST, rSubTreeSize, rSubTreeMin, rSubTreeMax := bstSum(root.Right, maxBSTSize) + isTreeBST = isRSubTreeBST && (root.Val < rSubTreeMin) + treeSize = 1 + rSubTreeSize + treeMin, treeMax = min2(root.Val, rSubTreeMin), max2(root.Val, rSubTreeMax) + } else { + isLSubTreeBST, lSubTreeSize, lSubTreeMin, lSubTreeMax := bstSum(root.Left, maxBSTSize) + isTreeBST = isLSubTreeBST && (root.Val > lSubTreeMax) + treeSize = 1 + lSubTreeSize + treeMin, treeMax = min2(root.Val, lSubTreeMin), max2(root.Val, lSubTreeMax) + } + + if root.Val == -2 { + fmt.Printf("treeMin: %v, treeMax: %v\n", treeMin, treeMax) + } + + if isTreeBST { + *maxBSTSize = max2(*maxBSTSize, treeSize) + } + + return isTreeBST, treeSize, treeMin, treeMax +} + +func largestBSTSubtree(root *TreeNode) int { + if root == nil { + return 0 + } + + maxBSTSum := 0 + + bstSum(root, &maxBSTSum) + + return maxBSTSum +} + +func test1() { + nodeM1 := TreeNode{Val: -1} + nodeM2 := TreeNode{Val: -2} + + nodeM2.Right = &nodeM1 + + result := largestBSTSubtree(&nodeM2) + + fmt.Printf("result: %d\n", result) +} + +func main() { + test1() +} diff --git a/largest-rectangle-in-histogram.go b/largest-rectangle-in-histogram.go new file mode 100644 index 0000000..53c851d --- /dev/null +++ b/largest-rectangle-in-histogram.go @@ -0,0 +1,74 @@ +package main + +import ( + "fmt" +) + +func max2(a, b int) int { + if a > b { + return a + } + + return b +} + +func largestRectangleArea(heights []int) int { + // heightStack := make([]int, 0, len(heights)+1) + indexStack := make([]int, 0, len(heights)+1) + maxArea := 0 + + indexStack = append(indexStack, -1) + + for i := 0; i < len(heights); i++ { + // We have an inversion + for indexStack[len(indexStack)-1] != -1 && heights[indexStack[len(indexStack)-1]] >= heights[i] { + h := heights[indexStack[len(indexStack)-1]] + indexStack = indexStack[:len(indexStack)-1] + + candidateArea := h * (i - indexStack[len(indexStack)-1] - 1) + maxArea = max2(maxArea, candidateArea) + } + + indexStack = append(indexStack, i) + } + + // We may still have some bars left in our stack - this will happen if there was no + // inversion at the end. Process them. + for indexStack[len(indexStack)-1] != -1 { + h := heights[indexStack[len(indexStack)-1]] + indexStack = indexStack[:len(indexStack)-1] + + candidateArea := h * (len(heights) - indexStack[len(indexStack)-1] - 1) + maxArea = max2(maxArea, candidateArea) + } + + return maxArea +} + +type testCase struct { + heights []int + expectedArea int +} + +func runTests() { + testCases := []testCase{ + testCase{heights: []int{1, 3, 2, 1, 2}, expectedArea: 5}, + testCase{heights: []int{4}, expectedArea: 4}, + testCase{heights: []int{2, 1, 5, 6, 2, 3}, expectedArea: 10}, + testCase{heights: []int{1, 3, 2}, expectedArea: 4}, + } + + for i, tc := range testCases { + result := largestRectangleArea(tc.heights) + + if result != tc.expectedArea { + fmt.Printf("Testcase %d FAILED; expected %d, got %d\n", i, tc.expectedArea, result) + } else { + fmt.Printf("Testcase %d PASSED\n", i) + } + } +} + +func main() { + runTests() +} diff --git a/lfu.go b/lfu.go new file mode 100644 index 0000000..7a962a9 --- /dev/null +++ b/lfu.go @@ -0,0 +1,361 @@ +package main + +import ( + "errors" + "fmt" +) + +type LinkedNode struct { + value interface{} + next *LinkedNode + prev *LinkedNode +} + +func (l *LinkedNode) IsEmpty() bool { + if l.next == nil && l.prev == nil { + return true + } + + if l.next == l && l.prev == l { + return true + } + + return false +} + +func (l *LinkedNode) AddNode(newNode *LinkedNode) { + if l.IsEmpty() { + l.next = newNode + l.prev = newNode + newNode.prev = l + newNode.next = l + } else { + currFirstNode := l.next + l.next = newNode + newNode.next = currFirstNode + newNode.prev = l + currFirstNode.prev = newNode + } +} + +// Should only be called from the head of the list (the head node is essentially the sentinel) +func (l *LinkedNode) Traverse(nodeVisitorFn func(data interface{})) { + for curr := l.next; curr != l; curr = curr.next { + nodeVisitorFn(curr.value) + } +} + +func DeleteNode(l *LinkedNode) error { + if l.IsEmpty() { + return errors.New("DeleteNode called on an empty list") + } + + next := l.next + prev := l.prev + + next.prev = prev + prev.next = next + + return nil +} + +func NewLinkedNode(data interface{}) *LinkedNode { + return &LinkedNode{value: data} +} + +func NewEmptyLinkedNode() *LinkedNode { + return &LinkedNode{} +} + +type LFUCache struct { + capacity int + size int + freqList *LinkedNode + keyMap map[int]valueDesc // int -> valueDesc +} + +type valueDesc struct { + valueNode *LinkedNode // Node containing the actual value + freqListNode *LinkedNode // Node from the freq list containing the value node +} + +type freqData struct { + freq int + valuesNodes *LinkedNode +} + +type keyValueData struct { + key int + value int +} + +func Constructor(capacity int) LFUCache { + return LFUCache{capacity: capacity, freqList: NewEmptyLinkedNode(), keyMap: make(map[int]valueDesc)} +} + +func (this *LFUCache) Get(key int) int { + vdesc, ok := this.keyMap[key] + + if !ok { + return -1 + } + + valueFreqNode := vdesc.freqListNode + valueFreqData := valueFreqNode.value.(freqData) + nextFreqNode := valueFreqNode.next + + nextFreqData := freqData{} + if nextFreqNode.value != nil { + nextFreqData = nextFreqNode.value.(freqData) + } + + var higherFreqNode *LinkedNode + + if nextFreqNode == this.freqList || nextFreqData.freq != valueFreqData.freq+1 { + // Either: + // - this particular freq node is the last one, or + // - the next higher freq node is for higher frequency than we need + // For both cases, create a new freq node. + newFreqData := freqData{freq: valueFreqData.freq + 1, valuesNodes: NewEmptyLinkedNode()} + newFreqNode := NewLinkedNode(newFreqData) + valueFreqNode.AddNode(newFreqNode) + higherFreqNode = newFreqNode + } else if nextFreqData.freq == valueFreqData.freq+1 { + higherFreqNode = nextFreqNode + } else { + panic("freqNode conditions not met") + } + + // Delete the value node from its current list and add it to the new freq node's value node list + DeleteNode(vdesc.valueNode) + higherFreqData := higherFreqNode.value.(freqData) + higherFreqData.valuesNodes.AddNode(vdesc.valueNode) + + // We also need to update the freq node since it has changed + vdesc.freqListNode = higherFreqNode + this.keyMap[key] = vdesc + + // If the old freq node's value node list is empty, we delete the freq node itself + if valueFreqData.valuesNodes.IsEmpty() { + DeleteNode(valueFreqNode) + } + + return vdesc.valueNode.value.(keyValueData).value +} + +func (this *LFUCache) Put(key int, value int) { + if this.Get(key) != -1 { + vdesc, ok := this.keyMap[key] + + if !ok { + panic(fmt.Sprintf("We should have found the %d in keyMap", key)) + } + + kvData := vdesc.valueNode.value.(keyValueData) + kvData.value = value + vdesc.valueNode.value = kvData + + return + } + + // if ok { + // // Update this value. No changes needed to freqList + // kvData := vdesc.valueNode.value.(keyValueData) + // kvData.value = value + // vdesc.valueNode.value = kvData + + // return + // } + + if this.capacity == 0 { + return + } + + // TODO: remove node if we're at capacity + if this.size == this.capacity { + // Delete one node to make space + firstFreqNode := this.freqList.next + firstFreqData := firstFreqNode.value.(freqData) + firstFreqValuesNodes := firstFreqData.valuesNodes + + // Delete the last (oldest) node in the valuesNodes pointed at by this freqNode + valueNodeToDelete := firstFreqValuesNodes.prev + DeleteNode(valueNodeToDelete) + + if firstFreqValuesNodes.IsEmpty() { + DeleteNode(firstFreqNode) + } + + delete(this.keyMap, valueNodeToDelete.value.(keyValueData).key) + this.size-- + } + + newVDesc := valueDesc{} + newValueNode := NewLinkedNode(keyValueData{key: key, value: value}) + newVDesc.valueNode = newValueNode + + // key not found in map: + // check if there's a freq node that has never been accessed. If + // there's one, the new value will go into that freq node's list. + // Otherwise, we'll need to create a new freq node and add this new + // value node to that freq node's list. + if this.freqList.IsEmpty() { + newValuesNodes := NewEmptyLinkedNode() + newValuesNodes.AddNode(newValueNode) + newFreqData := freqData{freq: 0, valuesNodes: newValuesNodes} + newFreqNode := NewLinkedNode(newFreqData) + this.freqList.AddNode(newFreqNode) + newVDesc.freqListNode = newFreqNode + } else { + // does the first node have freq 0? If so, we just add to that freqNode list + firstFreqNode := this.freqList.next + // We want to panic if the types are wrong + firstFreqData := firstFreqNode.value.(freqData) + + if firstFreqData.freq == 0 { + firstFreqValuesNodes := firstFreqData.valuesNodes + firstFreqValuesNodes.AddNode(newValueNode) + newVDesc.freqListNode = firstFreqNode + } else { + // We need to add new freq node with freq 0 + newValuesNodes := NewEmptyLinkedNode() + newValuesNodes.AddNode(newValueNode) + newFreqData := freqData{freq: 0, valuesNodes: newValuesNodes} + newFreqNode := NewLinkedNode(newFreqData) + this.freqList.AddNode(newFreqNode) + newVDesc.freqListNode = newFreqNode + } + } + + this.keyMap[key] = newVDesc + + this.size++ +} + +func printFreqList(freqList *LinkedNode) { + freqNodeVisitor := func(d interface{}) { + fd := d.(freqData) + + fmt.Printf("freq: %d\n", fd.freq) + + valueNodeVisitor := func(d interface{}) { + kvData := d.(keyValueData) + + fmt.Printf("\t%d -> %d\n", kvData.key, kvData.value) + } + + fd.valuesNodes.Traverse(valueNodeVisitor) + } + + freqList.Traverse(freqNodeVisitor) +} + +func printValues(head *LinkedNode) { + visitorFn := func(d interface{}) { + num, ok := d.(int) + + if !ok { + fmt.Printf("Unexpected type\n") + } + + fmt.Printf("Data: %d\n", num) + } + + head.Traverse(visitorFn) +} + +func test1() { + lfuCache := Constructor(2) + + lfuCache.Put(1, 1) + lfuCache.Put(2, 2) + lfuCache.Put(3, 3) + + // cache.put(1, 1); + // cache.put(2, 2); + // cache.get(1); // returns 1 + // cache.put(3, 3); // evicts key 2 + // cache.get(2); // returns -1 (not found) + // cache.get(3); // returns 3. + // cache.put(4, 4); // evicts key 1. + // cache.get(1); // returns -1 (not found) + // cache.get(3); // returns 3 + // cache.get(4); + + fmt.Printf("lfuCache.Get(1) = %d\n", lfuCache.Get(1)) + fmt.Printf("lfuCache.Get(2) = %d\n", lfuCache.Get(2)) + fmt.Printf("lfuCache.Get(2) = %d\n", lfuCache.Get(2)) + fmt.Printf("lfuCache.Get(3) = %d\n", lfuCache.Get(3)) + fmt.Printf("lfuCache.Get(3) = %d\n", lfuCache.Get(3)) + + // firstFreqNode := lfuCache.freqList.next + // firstFreqData := firstFreqNode.value.(freqData) + // printValues(firstFreqData.valuesNodes) + + printFreqList(lfuCache.freqList) +} + +func test2() { + lfuCache := Constructor(2) + + lfuCache.Put(1, 1) + lfuCache.Put(2, 2) + fmt.Printf("lfuCache.Get(1) = %d (expect 1)\n", lfuCache.Get(1)) + lfuCache.Put(3, 3) + fmt.Printf("lfuCache.Get(2) = %d (expect -1)\n", lfuCache.Get(2)) + fmt.Printf("lfuCache.Get(3) = %d (expect 3)\n", lfuCache.Get(3)) + lfuCache.Put(4, 4) + fmt.Printf("lfuCache.Get(1) = %d (expect -1)\n", lfuCache.Get(1)) + fmt.Printf("lfuCache.Get(3) = %d (expect 3)\n", lfuCache.Get(3)) + fmt.Printf("lfuCache.Get(4) = %d (expect 4)\n", lfuCache.Get(4)) + + // cache.put(1, 1); + // cache.put(2, 2); + // cache.get(1); // returns 1 + // cache.put(3, 3); // evicts key 2 + // cache.get(2); // returns -1 (not found) + // cache.get(3); // returns 3. + // cache.put(4, 4); // evicts key 1. + // cache.get(1); // returns -1 (not found) + // cache.get(3); // returns 3 + // cache.get(4); + + printFreqList(lfuCache.freqList) +} + +func test3() { + // ["LFUCache","put","put","put","put","get"] + //[[2],[3,1],[2,1],[2,2],[4,4],[2]] + lfuCache := Constructor(2) + + lfuCache.Put(3, 1) + lfuCache.Put(2, 1) + lfuCache.Put(2, 2) + lfuCache.Put(4, 4) + fmt.Printf("lfuCache.Get(2) = %d (expect 2)\n", lfuCache.Get(2)) + + printFreqList(lfuCache.freqList) +} + +func test4() { + // ["LFUCache","put","put","put","put","get","get"] + // [[2],[2,1],[1,1],[2,3],[4,1],[1],[2]] + lfuCache := Constructor(2) + + lfuCache.Put(2, 1) + lfuCache.Put(1, 1) + lfuCache.Put(2, 3) + lfuCache.Put(4, 1) + // printFreqList(lfuCache.freqList) + fmt.Printf("lfuCache.Get(1) = %d (expect -1)\n", lfuCache.Get(1)) + fmt.Printf("lfuCache.Get(2) = %d (expect 3)\n", lfuCache.Get(2)) + + printFreqList(lfuCache.freqList) +} + +func main() { + // test1() + // test2() + // test3() + test4() +} diff --git a/longest-consecutive-sequence.go b/longest-consecutive-sequence.go new file mode 100644 index 0000000..ba9697c --- /dev/null +++ b/longest-consecutive-sequence.go @@ -0,0 +1,99 @@ +package main + +import "fmt" + +func max2(a, b int) int { + if a > b { + return a + } + + return b +} + +func longestConsecutive(nums []int) int { + if len(nums) == 0 { + return 0 + } + + numIndices := make(map[int]int) + + for i, n := range nums { + numIndices[n] = i + } + + distinctNums := make([]int, 0, len(nums)) + + for k, _ := range numIndices { + distinctNums = append(distinctNums, k) + } + + for i, n := range distinctNums { + numIndices[n] = i + } + + consecStartLens := make(map[int]int) + path := make([]int, 0, len(distinctNums)) + + for k, _ := range numIndices { + _, ok := numIndices[k+1] + if !ok { + consecStartLens[k] = 1 + } + } + + maxConsecLen := 1 + + for k, _ := range numIndices { + curr := k + kPath := path[:] + + for { + consecLen := consecStartLens[curr] + + if consecLen >= 1 { + // We've reached a point starting from which we know how many + // consecutive elements follow + kPathLen := len(kPath) + for i, kp := range kPath { + consecStartLens[kp] = kPathLen - i + consecLen + maxConsecLen = max2(maxConsecLen, consecStartLens[kp]) + } + + break + } else { + kPath = append(kPath, curr) + } + + curr += 1 + } + } + + return maxConsecLen +} + +func test1() { + nums := []int{100, 4, 200, 1, 3, 2} + result := longestConsecutive(nums) + + fmt.Printf("result: %d\n", result) +} + +func test2() { + nums := []int{100, -4, 200, -1, 3, 2} + result := longestConsecutive(nums) + + fmt.Printf("result: %d\n", result) +} + +func test3() { + nums := []int{100, -4, 200} + result := longestConsecutive(nums) + + fmt.Printf("result: %d\n", result) +} + +func main() { + // test1() + // test2() + test3() +} diff --git a/longest-palindromic-substring.go b/longest-palindromic-substring.go new file mode 100644 index 0000000..e887bd5 --- /dev/null +++ b/longest-palindromic-substring.go @@ -0,0 +1,69 @@ +package main + +import "fmt" + +func longestPalindrome(s string) string { + sChars := []rune(s) + n := len(s) + dp := make([][]bool, n+1) + + if n == 0 { + return "" + } + + for i := 0; i < len(dp); i++ { + dp[i] = make([]bool, n) + } + + // All substrings of length 1 are palindromic by definition + maxPalindromeStartOffset := 0 + maxPalindromeLength := 0 + + for i := 0; i < n; i++ { + dp[1][i] = true + maxPalindromeLength = 1 + } + + for l := 2; l <= n; l++ { + for i := 0; i < n; i++ { + enclStart := i + 1 + enclEnd := i + l - 2 + + if enclEnd >= n || enclStart >= n || i+l-1 >= n { + break + } + + if enclStart >= enclEnd { + dp[l][i] = sChars[i] == sChars[i+l-1] + } else { + dp[l][i] = dp[l-2][i+1] && (sChars[i] == sChars[i+l-1]) + } + + if dp[l][i] { + maxPalindromeStartOffset = i + maxPalindromeLength = l + } + } + } + + return string(sChars[maxPalindromeStartOffset : maxPalindromeStartOffset+maxPalindromeLength]) +} + +func tests() { + testCases := []string{ + "babad", + "cbbd", + "", + "a", + "aaaaa", + "", + } + + for i := 0; i < len(testCases); i++ { + fmt.Printf("tc #%d: result: %s\n", i, longestPalindrome(testCases[i])) + } +} + +func main() { + tests() +} diff --git a/maximum-subarray.go b/maximum-subarray.go new file mode 100644 index 0000000..040f236 --- /dev/null +++ b/maximum-subarray.go @@ -0,0 +1,61 @@ +package main + +import "fmt" + +func max2(a, b int) int { + if a > b { + return a + } + + return b +} + +func maxSubArray(nums []int) int { + if len(nums) == 0 { + return 0 + } + + maxSum := nums[0] + sum := 0 + end := 0 + + for end < len(nums) { + if nums[end]+sum < 0 { + maxSum = max2(maxSum, sum+nums[end]) + end++ + sum = 0 + + continue + } + + maxSum = max2(maxSum, sum+nums[end]) + sum += nums[end] + end++ + } + + return maxSum +} + +func test1() { + tc := []int{-2, 1, -3, 4, -1, 2, 1, -5, 4} + + fmt.Printf("maxSum = %d\n", maxSubArray(tc)) +} + +func test2() { + tc := []int{-3, -2, 1} + + fmt.Printf("maxSum = %d\n", maxSubArray(tc)) +} + +func test3() { + tc := []int{4, -1, 2, 1} + + fmt.Printf("maxSum = %d\n", maxSubArray(tc)) +} + +func main() { + test1() + test2() + test3() +} diff --git a/minimum-window-substring.go b/minimum-window-substring.go new file mode 100644 index 0000000..1c065ef --- /dev/null +++ b/minimum-window-substring.go @@ -0,0 +1,239 @@ +package main + +import ( + "fmt" + "math" +) + +func minWindowUniqueTChars(s string, t string) string { + if len(t) == 0 || len(s) == 0 { + return "" + } + + tLUT := make([]int, 256) + tBytes := []rune(t) + + for _, c := range tBytes { + tLUT[byte(c)] = 1 + } + + sChars := []rune(s) + windowCharCount := make([]int, 256) + charPresence := make([]int, 256) + charPresenceCount := 0 + + start, end := 0, 0 + minWindowSize := math.MaxInt32 + minWindowStart, minWindowEnd := -1, -1 + + if tLUT[int(sChars[start])] == 1 { + windowCharCount[int(sChars[start])]++ + charPresence[int(sChars[start])] = 1 + charPresenceCount++ + + if charPresenceCount == len(t) { + if minWindowSize > (end - start + 1) { + minWindowSize = end - start + 1 + minWindowStart, minWindowEnd = start, end + } + } + } + + for end < len(s) { + if charPresenceCount < len(t) { + end++ + + if end >= len(s) { + break + } + + // Add the character at end to our presence counts + if tLUT[int(sChars[end])] == 1 { + windowCharCount[int(sChars[end])]++ + + if windowCharCount[int(sChars[end])] == 1 { + charPresence[int(sChars[end])] = 1 + charPresenceCount++ + } + } + } else { + start++ + + if start > 0 && tLUT[int(sChars[start-1])] == 1 { + windowCharCount[int(sChars[start-1])]-- + + if windowCharCount[int(sChars[start-1])] == 0 { + charPresence[int(sChars[start-1])] = 0 + charPresenceCount-- + } + } + + if start > end { + end = start + + if end >= len(s) { + break + } + + if tLUT[int(sChars[end])] == 1 { + windowCharCount[int(sChars[end])]++ + + if windowCharCount[int(sChars[end])] == 1 { + charPresence[int(sChars[end])] = 1 + charPresenceCount++ + } + } + } + } + + if charPresenceCount == len(t) { + if minWindowSize > (end - start + 1) { + minWindowSize = end - start + 1 + minWindowStart, minWindowEnd = start, end + } + } + } + + if minWindowSize == 0 || minWindowSize == math.MaxInt32 { + return "" + } + + return string(sChars[minWindowStart : minWindowEnd+1]) +} + +func minWindow(s string, t string) string { + if len(t) == 0 || len(s) == 0 { + return "" + } + + tLUT := make([]int, 256) + uniqueLUT := make([]int, 256) + uniqueTChars := 0 + tBytes := []rune(t) + + for _, c := range tBytes { + tLUT[int(c)]++ + + if uniqueLUT[int(c)] == 0 { + uniqueTChars++ + } + + uniqueLUT[int(c)] = 1 + } + + sChars := []rune(s) + windowCharCount := make([]int, 256) + charPresenceCount := 0 + + start, end := 0, 0 + minWindowSize := math.MaxInt32 + minWindowStart, minWindowEnd := -1, -1 + + if tLUT[int(sChars[start])] > 0 { + windowCharCount[int(sChars[start])]++ + + if windowCharCount[int(sChars[start])] == tLUT[int(sChars[start])] { + charPresenceCount++ + } + + if charPresenceCount == uniqueTChars { + if minWindowSize > (end - start + 1) { + minWindowSize = end - start + 1 + minWindowStart, minWindowEnd = start, end + } + } + } + + for end < len(s) { + if charPresenceCount < uniqueTChars { + end++ + + if end >= len(s) { + break + } + + // Add the character at end to our presence counts + if tLUT[int(sChars[end])] > 0 { + windowCharCount[int(sChars[end])]++ + + if windowCharCount[int(sChars[end])] == tLUT[int(sChars[end])] { + charPresenceCount++ + } + } + } else { + start++ + + if start > 0 && tLUT[int(sChars[start-1])] > 0 { + windowCharCount[int(sChars[start-1])]-- + + if windowCharCount[int(sChars[start-1])] == tLUT[int(sChars[start-1])]-1 { + charPresenceCount-- + } + } + + if start > end { + end = start + + if end >= len(s) { + break + } + + if tLUT[int(sChars[end])] > 0 { + windowCharCount[int(sChars[end])]++ + + if windowCharCount[int(sChars[end])] == tLUT[int(sChars[end])] { + charPresenceCount++ + } + } + } + } + + if charPresenceCount == uniqueTChars { + if minWindowSize > (end - start + 1) { + minWindowSize = end - start + 1 + minWindowStart, minWindowEnd = start, end + } + } + } + + if minWindowSize == 0 || minWindowSize == math.MaxInt32 { + return "" + } + + return string(sChars[minWindowStart : minWindowEnd+1]) +} + +type testcase struct { + input string + search string + expected string +} + +func runTests() { + testcases := []testcase{ + testcase{input: "ADOBECODEBANC", search: "ABC", expected: "BANC"}, + testcase{input: "ABC", search: "ABC", expected: "ABC"}, + testcase{input: "", search: "ABC", expected: ""}, + testcase{input: "A", search: "ABC", expected: ""}, + testcase{input: "AC", search: "ABC", expected: ""}, + testcase{input: "ACB", search: "ABC", expected: "ACB"}, + testcase{input: "ACB", search: "", expected: ""}, + testcase{input: "OOADCOBOOO", search: "ABC", expected: "ADCOB"}, + testcase{input: "AOOOOOBC", search: "ABC", expected: "AOOOOOBC"}, + testcase{input: "OAOOOOOBC", search: "ABC", expected: "AOOOOOBC"}, + testcase{input: "AA", search: "AA", expected: "AA"}, + testcase{input: "AA", search: "AAA", expected: ""}, + } + + for i, tc := range testcases { + result := minWindow(tc.input, tc.search) + + if result != tc.expected { + fmt.Printf("TC %d (%s, %s) FAILED. Expected: %s; got: %s\n", i, tc.input, tc.search, tc.expected, result) + } + } +} + +func main() { + runTests() +} diff --git a/n-bishops.go b/n-bishops.go new file mode 100644 index 0000000..4505948 --- /dev/null +++ b/n-bishops.go @@ -0,0 +1,90 @@ +package main + +import ( + "bufio" + "fmt" + "os" +) + +type bishopPos struct { + row int + col int +} + +type board struct { + bishops []bishopPos + maxRows int + maxCols int +} + +func max(a, b int) int { + if a > b { + return a + } + + return b +} + +func readBishopPos() board { + scanner := bufio.NewScanner(os.Stdin) + maxRows, maxCols := -1, -1 + + scanner.Scan() + var n int + fmt.Sscanf(scanner.Text(), "%d", &n) + bps := make([]bishopPos, n) + + // fmt.Printf("Reading %d lines\n", n) + + for i := 0; i < n; i++ { + scanner.Scan() + fmt.Sscanf(scanner.Text(), "%d %d", &(bps[i].row), &(bps[i].col)) + maxCols = max(maxCols, bps[i].col) + } + + for _, bp := range bps { + diagL := bp.row + bp.col - 1 + diagR := bp.row + (maxCols - bp.col) + + maxRows = max(max(maxRows, diagL), diagR) + } + + return board{ + bishops: bps, + maxRows: maxRows, + maxCols: maxCols, + } +} + +func countCaptures(b board) int { + diagLCounts := make([]int, b.maxRows+1) + diagRCounts := make([]int, b.maxRows+1) + captureCount := 0 + + for _, bp := range b.bishops { + diagL := bp.row + bp.col - 1 + diagR := bp.row + (b.maxCols - bp.col) + + prevDiagLCount := diagLCounts[diagL] + prevDiagRCount := diagRCounts[diagR] + + diagLCounts[diagL]++ + diagRCounts[diagR]++ + + prevDiagLCaptures := prevDiagLCount * (prevDiagLCount - 1) / 2 + prevDiagRCaptures := prevDiagRCount * (prevDiagRCount - 1) / 2 + newDiagLCaptures := diagLCounts[diagL] * (diagLCounts[diagL] - 1) / 2 + newDiagRCaptures := diagRCounts[diagR] * (diagRCounts[diagR] - 1) / 2 + + captureCount = captureCount + (newDiagLCaptures - prevDiagLCaptures) + (newDiagRCaptures - prevDiagRCaptures) + } + + return captureCount +} + +func main() { + b := readBishopPos() + + // fmt.Printf("%v\n", b) + fmt.Printf("%d\n", countCaptures(b)) +} diff --git a/non-decreasing-array.go b/non-decreasing-array.go new file mode 100644 index 0000000..c3a876a --- /dev/null +++ b/non-decreasing-array.go @@ -0,0 +1,119 @@ +package main + +import ( + "fmt" +) + +/* +Cases: + +1. a < b < c -- do nothing +2. a < b > c + 2.1 a <= c -- set b = c + 2.2 a > c -- can't fix +3. a < b = c -- do nothing +4. a > b < c + 4.1 a <= c -- set b = c + 4.2 a > c -- can't fix +5. a > b > c -- can't fix +6. a > b = c -- can't fix +7. a = b < c -- do nothing +8. a = b > c -- can't fix +9. a = b = c -- do noting + +*/ + +func checkPossibility(nums []int) bool { + if len(nums) <= 2 { + return true + } + + a, b, c := 0, 0, 0 + numChanges := 0 + + for i := 0; i < len(nums); i++ { + if i == 0 { + b, c = nums[i], nums[i + 1] + + if b > c { + numChanges++ + b = c + nums[i] = b + } + + continue + } + + if i == len(nums) - 1 { + a, b = nums[i - 1], nums[i] + + if a > b { + numChanges++ + b = a + nums[i] = b + } + + continue + } + + a, b, c = nums[i-1], nums[i], nums[i + 1] + + // 2. a < b > c + // 2.1 a <= c -- set b = c + // 2.2 a > c -- can't fix + if (a < b && b > c) || (a > b && b < c) { + if a <= c { + numChanges++ + b = c + nums[i] = b + } + + continue + } + } + + // Check if non-decreasing + for i := 0; i < len(nums); i++ { + if i < len(nums) - 1 { + if nums[i] > nums[i + 1] { + return false + } + } + } + + return numChanges <= 1 +} + +type testcase struct { + nums []int + expectedResult bool +} + +func tests() { + tcs := []testcase { + testcase{nums: []int{4}, expectedResult: true}, + testcase{nums: []int{4,2}, expectedResult: true}, + testcase{nums: []int{4,2,3}, expectedResult: true}, + testcase{nums: []int{4,3,2}, expectedResult: false}, + testcase{nums: []int{1,1,3,2}, expectedResult: true}, + testcase{nums: []int{1,2,3,2}, expectedResult: true}, + testcase{nums: []int{1,2,3,2,5}, expectedResult: true}, + testcase{nums: []int{1,7,3,2,5}, expectedResult: false}, + testcase{nums: []int{1,7,3,2,5,5}, expectedResult: false}, + testcase{nums: []int{1,2,4,5,3}, expectedResult: true}, + } + + for i, tc := range tcs { + result := checkPossibility(tc.nums) + + if result == tc.expectedResult { + fmt.Printf("TC %d PASSED\n", i) + } else { + fmt.Printf("TC %d FAILED; expected: %v, actual: %v\n", i, tc.expectedResult, result) + } + } +} + +func main() { + tests() +} \ No newline at end of file diff --git a/number-of-islands.go b/number-of-islands.go new file mode 100644 index 0000000..f7c57cf --- /dev/null +++ b/number-of-islands.go @@ -0,0 +1,72 @@ +package main + +import ( + "fmt" +) + +const ( + UnvisitedLand = '1' + VisitedLand = '2' +) + +func dfs(grid [][]byte, atR, atC int) { + if grid[atR][atC] != UnvisitedLand { + return + } + + grid[atR][atC] = VisitedLand + + numRows := len(grid) + numCols := len(grid[0]) + dirR := []int{-1, 1, 0, 0} + dirC := []int{ 0, 0,-1, 1} + + for i := 0; i < len(dirR); i++ { + newR, newC := atR + dirR[i], atC + dirC[i] + + if newR < 0 || newR >= numRows || newC < 0 || newC >= numCols { + continue + } + + dfs(grid, newR, newC) + } +} + +func numIslands(grid [][]byte) int { + n := 0 + numRows := len(grid) + + if numRows == 0 { + return 0 + } + + numCols := len(grid[0]) + + for i := 0; i < numRows; i++ { + for j := 0; j < numCols; j++ { + if grid[i][j] == UnvisitedLand { + n++ + dfs(grid, i, j) + } + } + } + + return n +} + +func test1() { + grid := [][]byte { + []byte {'1','1','1','1','0'}, + []byte {'1','1','0','1','0'}, + []byte {'1','1','0','0','0'}, + []byte {'0','0','0','0','0'}, + } + + fmt.Printf("numIslands: %d\n", numIslands(grid)) +} + + + +func main() { + test1() +} diff --git a/partition-equal-subset-sum.go b/partition-equal-subset-sum.go new file mode 100644 index 0000000..80a0bc5 --- /dev/null +++ b/partition-equal-subset-sum.go @@ -0,0 +1,74 @@ +package main + +import "fmt" + +func canPartition(nums []int) bool { + sum := 0 + + for _, n := range nums { + sum += n + } + + if sum%2 != 0 { + return false + } + + halfSum := sum / 2 + part := make([][]bool, halfSum+1) + n := len(nums) + + for i := 0; i <= halfSum; i++ { + part[i] = make([]bool, n+1) + } + + for i := 0; i <= n; i++ { + part[0][i] = true + } + + for s := 1; s <= halfSum; s++ { + part[s][0] = false + + for j := 1; j <= n; j++ { + if part[s][j-1] { + part[s][j] = true + + continue + } + + if s-nums[j-1] >= 0 { + // parts[s][j] is true if there's a subset of nums[0 .. j - 1] that sums to s. + // In other words, if there's a subset of nums[0 .. j - 2] that sums to s - nums[j - 1]. + part[s][j] = part[s-nums[j-1]][j-1] + } + } + } + + return part[halfSum][n] +} + +type testCase struct { + nums []int + expected bool +} + +func tests() { + testCases := []testCase{ + testCase{nums: []int{1, 3, 2}, expected: true}, + testCase{nums: []int{1, 5, 11, 5}, expected: true}, + testCase{nums: []int{1, 2, 3, 5}, expected: false}, + } + + for i, tc := range testCases { + result := canPartition(tc.nums) + + if result == tc.expected { + fmt.Printf("Test number %d: PASS\n", i) + } else { + fmt.Printf("Test number %d: FAILED (expected: %v, got %v)\n", i, tc.expected, result) + } + } +} + +func main() { + tests() +} diff --git a/reconstruct-itinerary.go b/reconstruct-itinerary.go new file mode 100644 index 0000000..db2776f --- /dev/null +++ b/reconstruct-itinerary.go @@ -0,0 +1,150 @@ +package main + +// reference: https://en.wikipedia.org/wiki/Eulerian_path + +import ( + "fmt" + "sort" +) + +type AirportCodes []string + +func (ac AirportCodes) Len() int { + return len(ac) +} + +func (ac AirportCodes) Swap(i, j int) { + ac[i], ac[j] = ac[j], ac[i] +} + +func (ac AirportCodes) Less(i, j int) bool { + return ac[i] < ac[j] // strings.Compare(ac[i], ac[j]) < 0 +} + +func dfs(adjMat [][]int, from int, acs []string, traversed []string) []string { + for to := 0; to < len(adjMat[from]); { + if adjMat[from][to] <= 0 { + to++ + continue + } + + adjMat[from][to]-- + + if adjMat[from][to] == 0 { + adjMat[from][to] = -1 + } + + traversed = dfs(adjMat, to, acs, traversed) + } + + return append(traversed, acs[from]) +} + +func reverse(strings []string) { + for i, j := 0, len(strings)-1; i < j; i, j = i+1, j-1 { + strings[i], strings[j] = strings[j], strings[i] + } +} + +func findItinerary(tickets [][]string) []string { + acOffsets := make(map[string]int) + + for _, srcDest := range tickets { + src, dest := srcDest[0], srcDest[1] + + acOffsets[src] = -1 + acOffsets[dest] = -1 + } + + acs := make([]string, len(acOffsets)) + acsOffset := 0 + + for k, _ := range acOffsets { + acs[acsOffset] = k + acsOffset++ + } + + sort.Sort(AirportCodes(acs)) + + for i, ac := range acs { + acOffsets[ac] = i + } + + fmt.Printf("acOffsets: %v\n", acOffsets) + + numAirports := len(acs) + adjMat := make([][]int, numAirports) + + for i := 0; i < numAirports; i++ { + adjMat[i] = make([]int, numAirports) + } + + for _, ticket := range tickets { + from, to := acOffsets[ticket[0]], acOffsets[ticket[1]] + adjMat[from][to]++ + } + + traversed := dfs(adjMat, acOffsets["JFK"], acs, []string{}) + + reverse(traversed) + + return traversed +} + +func test1() { + tickets := [][]string{ + []string{"MUC", "LHR"}, + []string{"JFK", "MUC"}, + []string{"SFO", "SJC"}, + []string{"LHR", "SFO"}, + } + + fmt.Printf("itinerary: %v\n", findItinerary(tickets)) +} + +func test2() { + tickets := [][]string{ + []string{"JFK", "LHR"}, + []string{"LHR", "SFO"}, + []string{"SFO", "JFK"}, + []string{"JFK", "ATL"}, + } + + fmt.Printf("itinerary: %v\n", findItinerary(tickets)) +} + +func test3() { + tickets := [][]string{ + []string{"JFK", "AAA"}, + []string{"AAA", "JFK"}, + []string{"JFK", "BBB"}, + []string{"JFK", "CCC"}, + []string{"CCC", "JFK"}, + } + + fmt.Printf("itinerary: %v\n", findItinerary(tickets)) +} + +func test4() { + tickets := [][]string{ + []string{"JFK", "SFO"}, []string{"JFK", "ATL"}, []string{"SFO", "ATL"}, []string{"ATL", "JFK"}, []string{"ATL", "SFO"}, + } + + fmt.Printf("itinerary: %v\n", findItinerary(tickets)) +} + +func tests() { + airportCodes := AirportCodes([]string{"JFK", "ATL", "SFO"}) + + sort.Sort(airportCodes) + + fmt.Printf("sorted codes: %v\n", airportCodes) +} + +func main() { + // tests() + // test1() + // test2() + // test3() + test4() +} diff --git a/remove-invalid-parentheses.go b/remove-invalid-parentheses.go new file mode 100644 index 0000000..8e8e2b2 --- /dev/null +++ b/remove-invalid-parentheses.go @@ -0,0 +1,154 @@ +package main + +import ( + "fmt" +) + +func mismatchedParens(s string) (int, int) { + lParens, rParens := 0, 0 + chars := []rune(s) + mismatchedLParens, mismatchedRParens := 0, 0 + + for _, c := range chars { + if c == '(' { + lParens++ + } + + if c == ')' { + rParens++ + + if lParens == 0 { + mismatchedRParens++ + } else { + lParens-- + rParens-- + } + } + + } + + mismatchedLParens = lParens + + return mismatchedLParens, mismatchedRParens +} + +func removeParensAt(chars []rune, at, lParensToDelete, rParensToDelete int, results map[string]struct{}) map[string]struct{} { + if at >= len(chars) || at < 0 { + if lParensToDelete == 0 && rParensToDelete == 0 { + // Check if balanced and add to results if it is + s := string(chars) + mismatchedL, mismatchedR := mismatchedParens(s) + + if mismatchedL == 0 && mismatchedR == 0 { + results[s] = struct{}{} + } + } + + return results + } + + c := chars[at] + + if c != '(' && c != ')' { + return removeParensAt(chars, at+1, lParensToDelete, rParensToDelete, results) + } + + if c == '(' { + if lParensToDelete > 0 { + atCharDeleted := []rune{} + atCharDeleted = append(atCharDeleted, chars[0:at]...) + if at < len(chars)-1 { + atCharDeleted = append(atCharDeleted, chars[at+1:]...) + } + + results = removeParensAt(atCharDeleted, at, lParensToDelete-1, rParensToDelete, results) + } + } else { + if rParensToDelete > 0 { + atCharDeleted := []rune{} + atCharDeleted = append(atCharDeleted, chars[0:at]...) + if at < len(chars)-1 { + atCharDeleted = append(atCharDeleted, chars[at+1:]...) + } + + results = removeParensAt(atCharDeleted, at, lParensToDelete, rParensToDelete-1, results) + } + } + + unmodified := []rune(string(chars)) + results = removeParensAt(unmodified, at+1, lParensToDelete, rParensToDelete, results) + + return results +} + +func removeInvalidParentheses(s string) []string { + mismatchedL, mismatchedR := mismatchedParens(s) + + if mismatchedL == 0 && mismatchedR == 0 || len(s) == 0 { + return []string{s} + } + + results := make(map[string]struct{}) + results = removeParensAt([]rune(s), 0, mismatchedL, mismatchedR, results) + + resultList := []string{} + for k, _ := range results { + resultList = append(resultList, k) + } + + return resultList +} + +func testMismatchedParens() { + testStrs := []string{ + "( ) ) ) ( ( ( )", // 0 + "(", // 1 + ")", // 2 + "( ( )", // 3 + "( ) )", // 4 + "( ( ) )", // 5 + "( ) ( )", // 6 + "( ( ) ) )", // 7 + "( ) ( ) ) ( )", // 8 + } + + for i, ts := range testStrs { + mL, mR := mismatchedParens(ts) + + fmt.Printf("Test %d: mL = %d, mR = %d\n", i, mL, mR) + } +} + +func testRemoveInvalidParens() { + testStrs := []string{ + // "( ) ) ) ( ( ( )", // 0 + // "(", // 1 + // ")", // 2 + "(a(b)", // 3 + // "( ) )", // 4 + "(())", // 5 + // "( ) ( )", // 6 + // "( ( ) ) )", // 7 + "(a)())()", // 8 + "()())()", // 9 + ")(", // 10 + } + + for i, ts := range testStrs { + results := removeInvalidParentheses(ts) + + fmt.Printf("Test %d:", i) + + for _, result := range results { + fmt.Printf(" \"%s\" ", result) + + } + + fmt.Println() + } +} + +func main() { + // testMismatchedParens() + testRemoveInvalidParens() +} diff --git a/squares-of-a-sorted-array.go b/squares-of-a-sorted-array.go new file mode 100644 index 0000000..36402a4 --- /dev/null +++ b/squares-of-a-sorted-array.go @@ -0,0 +1,36 @@ +package main + +import "fmt" + +func sortedSquares(A []int) []int { + i, j, k := 0, len(A)-1, len(A)-1 + result := make([]int, len(A)) + + for i <= j { + if A[i]*A[i] > A[j]*A[j] { + A[k] = A[i] * A[i] + i++ + k-- + + continue + } + + result[k] = A[j] * A[j] + j-- + k-- + } + + return result +} + +func runTests() { + fmt.Printf("sortedSquares: %v\n", sortedSquares([]int{-4, -1, 0, 3, 10})) + fmt.Printf("sortedSquares: %v\n", sortedSquares([]int{})) + fmt.Printf("sortedSquares: %v\n", sortedSquares([]int{1})) + fmt.Printf("sortedSquares: %v\n", sortedSquares([]int{-1})) + fmt.Printf("sortedSquares: %v\n", sortedSquares([]int{-7, -3, 2, 3, 11})) +} + +func main() { + runTests() +} diff --git a/subtree-of-another-tree.go b/subtree-of-another-tree.go new file mode 100644 index 0000000..681bbaa --- /dev/null +++ b/subtree-of-another-tree.go @@ -0,0 +1,49 @@ +package main + +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +func isIdentical(t1 *TreeNode, t2 *TreeNode) bool { + if t1 == nil { + return t2 == nil + } + + if t2 == nil { + return false + } + + return t1.Val == t2.Val && isIdentical(t1.Left, t2.Left) && isIdentical(t1.Right, t2.Right) +} + +func isSubtree(s *TreeNode, t *TreeNode) bool { + if t == nil { + // bug in Leetcode's solution checked. A nil t should be + // considered a subtree of any s. + return s == nil + } + + if isIdentical(s, t) { + return true + } + + if s == nil { + return false + } + + return isSubtree(s.Left, t) || isSubtree(s.Right, t) +} + +func main() { + +} diff --git a/ugly-number-2.go b/ugly-number-2.go new file mode 100644 index 0000000..ed33393 --- /dev/null +++ b/ugly-number-2.go @@ -0,0 +1,64 @@ +package main + +import "fmt" + +func min3(a, b, c int) int { + if a < b { + if a < c { + return a + } + + return c + } + + if b < c { + return b + } + + return c +} + +func nthUglyNumber(n int) int { + nums := make([]int, n) + x, y, z := 0, 0, 0 + nums[0] = 1 + next := 1 + + for next < len(nums) { + a := 2 * nums[x] + b := 3 * nums[y] + c := 5 * nums[z] + + min := min3(a, b, c) + nums[next] = min + next++ + + if a == min { + x++ + } + + if b == min { + y++ + } + + if c == min { + z++ + } + } + + return nums[len(nums)-1] +} + +func test() { + for i := 1; i < 11; i++ { + uglyNumber := nthUglyNumber(i) + + fmt.Printf("%d ", uglyNumber) + } + + fmt.Printf("\n") +} + +func main() { + test() +} diff --git a/unique-paths.go b/unique-paths.go new file mode 100644 index 0000000..2a32797 --- /dev/null +++ b/unique-paths.go @@ -0,0 +1,61 @@ +package main + +import "fmt" + +func uniquePaths(m int, n int) int { + if m == 0 || n == 0 { + return 0 + } + + grid := make([][]int, m) + + for i, _ := range grid { + grid[i] = make([]int, n) + } + + grid[0][0] = 1 + + for i := 0; i < m; i++ { + for j := 0; j < n; j++ { + rightR, rightC := i, j+1 + downR, downC := i+1, j + + if rightC < n { + grid[rightR][rightC] += grid[i][j] + } + + if downR < m { + grid[downR][downC] += grid[i][j] + } + } + } + + return grid[m-1][n-1] +} + +type testcase struct { + m int + n int + expectedResult int +} + +func tests() { + testcases := []testcase{ + testcase{m: 3, n: 2, expectedResult: 3}, + testcase{m: 7, n: 3, expectedResult: 28}, + } + + for i, tc := range testcases { + result := uniquePaths(tc.m, tc.n) + + if result != tc.expectedResult { + fmt.Printf("Test %d FAILED (expected: %d; actual %d)\n", i, tc.expectedResult, result) + } else { + fmt.Printf("Test %d PASSED\n", i) + } + } +} + +func main() { + tests() +} diff --git a/word-search-2.go b/word-search-2.go new file mode 100644 index 0000000..74eedc0 --- /dev/null +++ b/word-search-2.go @@ -0,0 +1,141 @@ +package main + +import "fmt" + +func runeToByteBoard(rBoard [][]rune) [][]byte { + rows := len(rBoard) + cols := len(rBoard[0]) + bBoard := make([][]byte, rows) + + for r := 0; r < rows; r++ { + bBoard[r] = make([]byte, cols) + + for c := 0; c < cols; c++ { + bBoard[r][c] = byte(rBoard[r][c]) + } + } + + return bBoard +} + +func clearTraversed(traversed [][]bool) { + for r := 0; r < len(traversed); r++ { + for c := 0; c < len(traversed[0]); c++ { + traversed[r][c] = false + } + } +} + +func checkTraversed(traversed [][]bool) { + for r := 0; r < len(traversed); r++ { + for c := 0; c < len(traversed[0]); c++ { + if traversed[r][c] { + fmt.Printf("traversed[%d][%d] is true", r, c) + + return + } + } + } +} + +func checkForWord(board [][]byte, traversed [][]bool, word []rune, fromRow, fromCol, start int) bool { + if start >= len(word) { + return true + } + + if board[fromRow][fromCol] != byte(word[start]) { + return false + } + + if start == len(word)-1 { + return true + } + + dirR := []int{0, 0, -1, 1} + dirC := []int{-1, 1, 0, 0} + numRows := len(board) + numCols := len(board[0]) + + for d := 0; d < len(dirR); d++ { + newR, newC := fromRow+dirR[d], fromCol+dirC[d] + + if newR < 0 || newR >= numRows || newC < 0 || newC >= numCols || traversed[newR][newC] { + continue + } + + traversed[newR][newC] = true + + if checkForWord(board, traversed, word, newR, newC, start+1) { + traversed[newR][newC] = false + return true + } + + traversed[newR][newC] = false + } + + return false +} + +func findWords(board [][]byte, words []string) []string { + if len(board) == 0 { + return []string{} + } + + rows := len(board) + cols := len(board[0]) + result := []string{} + + traversed := make([][]bool, rows) + + for r := 0; r < rows; r++ { + traversed[r] = make([]bool, cols) + } + + for w := 0; w < len(words); w++ { + wordRunes := []rune(words[w]) + wordFound := false + + for r := 0; r < rows && !wordFound; r++ { + for c := 0; c < cols && !wordFound; c++ { + // checkTraversed(traversed) + + if (byte)(wordRunes[0]) != board[r][c] { + continue + } + + // fmt.Printf("found ") + traversed[r][c] = true + if checkForWord(board, traversed, wordRunes, r, c, 0) { + traversed[r][c] = false + wordFound = true + break + } + traversed[r][c] = false + } + } + + if wordFound { + result = append(result, words[w]) + } + } + + return result +} + +func test1() { + rBoard := [][]rune{ + []rune{'o', 'a', 'a', 'n'}, + []rune{'e', 't', 'a', 'e'}, + []rune{'i', 'h', 'k', 'r'}, + []rune{'i', 'f', 'l', 'v'}, + } + bBoard := runeToByteBoard(rBoard) + words := []string{"oath", "pea", "eat", "rain"} + result := findWords(bBoard, words) + + fmt.Printf("result: %v\n", result) +} + +func main() { + test1() +}