diff --git a/array/README.md b/array/README.md index 8f3d3cc..9a5190d 100644 --- a/array/README.md +++ b/array/README.md @@ -6,7 +6,7 @@ To provide a real-world analogy, consider an array of athletes preparing for a s ## Implementation -In the Go programming language, arrays are considered values rather than pointers and represent the entirety of the array. Whenever an array is passed to a function, a copy is created, resulting in additional memory usage. To avoid this, it is possible to pass a pointer to an array, or use slices instead. The size of the array is constant and it must be known at compile time, and there is no need to use the built-in `make` function when defining arrays. +In the Go programming language, [arrays](https://go.dev/blog/slices) are considered values rather than pointers and represent the entirety of the array. Whenever an array is passed to a function, a copy is created, resulting in additional memory usage. To avoid this, it is possible to pass a pointer to an array, or use slices instead. The size of the array is constant and it must be known at compile time, and there is no need to use the built-in `make` function when defining arrays. ```Go package main diff --git a/strings/README.md b/strings/README.md index d457d32..47e8ef5 100644 --- a/strings/README.md +++ b/strings/README.md @@ -1,10 +1,12 @@ # String -A string is a ubiquitous data structure, typically a built-in data type in programming languages. However, beneath the surface, strings are essentially arrays of characters that enable textual data storage and manipulation. +A string is a ubiquitous data structure, typically a built-in data type in programming languages. However, beneath the surface, strings are essentially [slices](../array/) of characters that enable textual data storage and manipulation. ## Implementation -In Go, strings are a data type. Behind the scenes strings are a slice of bytes. The `strings` package provides several useful convenience functions. Examples include: +In Go, [strings](https://go.dev/blog/strings) are a data type. Behind the scenes strings are an immutable slice of bytes. Since Go is a UTF-8 compliant language, each character in Go can take up to 4 bytes of storage. + +The `strings` package provides several useful convenience functions. Examples include: * [Index](https://golang.org/pkg/strings/#Index), [Contains](https://golang.org/pkg/strings/#Contains), [HasPrefix](https://golang.org/pkg/strings/#HasPrefix), [HasSuffix](https://golang.org/pkg/strings/#HasSuffix) * [Split](https://golang.org/pkg/strings/#Split), [Fields](https://golang.org/pkg/strings/#Split), [Join](https://golang.org/pkg/strings/#Join) @@ -13,27 +15,96 @@ In Go, strings are a data type. Behind the scenes strings are a slice of bytes. * [Title](https://golang.org/pkg/strings/#Title), [ToLower](https://golang.org/pkg/strings/#ToLower), [ToUpper](https://golang.org/pkg/strings/#ToUpper) * [Trim](https://golang.org/pkg/strings/#Trim), [TrimSpace](https://golang.org/pkg/strings/#TrimSpace), [TrimSuffix](https://golang.org/pkg/strings/#TrimSuffix), [TrimPrefix](https://golang.org/pkg/strings/#TrimPrefix) -When a string is iterated in Go using the `range` keyword, every element becomes a [rune](https://blog.golang.org/strings#TOC_5.) which is an alias for the type `int32`. If the code being written works with many single-character strings, it is better to define variables and function parameters as `rune`. The following code shows how to iterate through a string. +When a iterating every character in a string in Go using the `range` keyword, every element becomes a [rune](https://blog.golang.org/strings#TOC_5.) which is an alias for the type `int32`. If the code being written works with many single-character strings, it is better to define variables and function parameters as `rune` rather than convert them many times. The following code shows how to iterate through a string. + +```Go +package main + +import "fmt" + +/* + main outputs the rune (int32) value of each character: + + Char #0 "a" has value 97 + Char #1 "A" has value 65 + Char #2 "和" has value 21644 + Char #5 "平" has value 24179 + Char #8 "😊" has value 128522 +*/ +func main() { + for i, r := range "aA𓅚😊" { + fmt.Printf("Char #%d %q has value %d\n", i, string(r), r) + } +} +``` + +A very common tool to use for manipulating strings in Go is the `fmt.Sprintf` function. This is specially useful when converting many values into a string. ```Go package main +import "fmt" + +func main() { + number := 1 + value := 1.1 + name := "foo" + + output := fmt.Sprintf("%d %f %s", number, value, name) + fmt.Println(output) // 1 1.100000 foo +} +``` + +### Regular Expressions + +Unlike many other programming languages, in Go [regular expressions](https://golang.org/pkg/regexp/) are [guaranteed](https://swtch.com/~rsc/regexp/regexp1.html) to have O(n) time complexity where n is the length of the input, making them a viable and practical option for pattern matching in a string. + +Here is an example of how you can find a pattern using regular expressions in Go. Given a string return the string if it contains a fish word. A fish word is a word that starts with `fi` optionally followed by other character(s) and ends with `sh`. Examples include {`fish`, `finish`}. + +```GO +package main + import ( "fmt" + "regexp" ) +var fishPattern = regexp.MustCompile(`(?i).*fi\w*sh\b`) + +// main outputs [selfish][shellfish][fish][finish][Finnish] func main() { - for i, r := range "abcd" { - fmt.Printf("Char #%d %q has value %d\n", i, string(r), r) + inputs := []string{"shift", "selfish", "shellfish", "fish dish", "finish", "Finnish"} + + for _, input := range inputs { + matches := fishPattern.FindAllString(input, -1) + if len(matches) > 0 { + fmt.Print(matches) + } } } ``` ## Complexity -Strings have the same complexity as [arrays](../array/) and slices in Go. +Since strings are slices of bytes, the time complexity of string operations should be similar to [arrays](../array/). Reading a character at a given index is O(1), but since strings are immutable modifying them involves creating a new string making it a O(n) operation. Go standard library includes `strings.Builder` for more efficient string building. + +The space complexity to store a string depends on the type of characters. This following example shows how we can index a string and print the hexadecimal value of every byte in it. + +```GO +package main + +import "fmt" + +// main Outputs 41 f0 93 85 9a f0 9f 98 8a. +func main() { + input := "A𓅚😊" + for i := 0; i < len(input); i++ { + fmt.Printf("%x ", input[i]) + } +} +``` -Unlike many other programming languages, [regular expressions](https://golang.org/pkg/regexp/) are guaranteed to have O(n) time complexity in Go, allowing efficient string pattern matching. +The output of the above code indicates that 9 bytes are used to store the 3 input characters. 1 byte for the first character and 4 bytes for each of the remaining two. ## Application diff --git a/strings/in_memory_database_test.go b/strings/in_memory_database_test.go index d32f9ce..ae437f6 100644 --- a/strings/in_memory_database_test.go +++ b/strings/in_memory_database_test.go @@ -12,6 +12,34 @@ TestInMemoryDictionary tests solution(s) with the following signature and proble Write an in memory database that stores string key-value pairs and supports SET, GET, EXISTS, and UNSET commands. It should also allow transactions with BEGIN, COMMIT and ROLLBACK commands. + +For example: + + GET A // outputs nil + BEGIN + SET A 1 + GET A // outputs 1 + COMMIT + GET A // outputs 1 + +At the first GET A, nil is returned because it has never been set. The second and third +GET A will output 1 because the value of A was set as 1. + +BEGIN, ROLLBACK and COMMIT are referred to as transactions in databases. A transaction is +started by BEGIN. The commands that are followed by a BEGIN are completely ignored if a ROLLBACK +command is given and actually applied only when COMMIT command is given. + +For example: + + BEGIN + SET A 1 + COMMIT + BEGIN + SET A 2 + ROLLBACK + GET A // outputs 1 + +The output is 1 because SET A 2 was never committed so the value of A remains 1. */ func TestInMemoryDictionary(t *testing.T) { tests := []struct { @@ -21,6 +49,7 @@ func TestInMemoryDictionary(t *testing.T) { {"EXISTS A\nSET A 1\nGET A\nEXISTS A\nUNSET A\nGET A\nEXISTS A", "false 1 true nil false"}, {"GET A\nBEGIN\nSET A 1\nGET A\nROLLBACK\nGET A", "nil 1 nil"}, {"GET A\nBEGIN\nSET A 1\nGET A\nCOMMIT\nGET A", "nil 1 1"}, + {"BEGIN\nSET A 1\nCOMMIT\nBEGIN\nSET A 2\nROLLBACK\nGET A", "1"}, {"SET A 1\nGET A\nBEGIN\nSET A 2\nGET A\nBEGIN\nUNSET A\nGET A\nCOMMIT\nROLLBACK\nGET A", "1 2 nil 1"}, {"SET A 2\nGET A\nBEGIN\nSET A 1\nGET A\nCOMMIT\nGET A\nBEGIN\nSET A 2\nGET A\nROLLBACK\nGET A", "2 1 1 2 1"}, } diff --git a/strings/longest_dictionary_word_test.go b/strings/longest_dictionary_word_test.go index 7446924..fa91667 100644 --- a/strings/longest_dictionary_word_test.go +++ b/strings/longest_dictionary_word_test.go @@ -7,8 +7,11 @@ TestLongestDictionaryWordContainingKey tests solution(s) with the following sign func LongestDictionaryWordContainingKey(key string, dic []string) string -Given a key like "car", and a dictionary like {"rectify", "race", "archeology", "racoon"} return the longest -dictionary word that contains every letter of the key like "archeology". +Given a key as string, and a slice of strings containing a dictionary of words, return the longest +word that contains all letters of the key. + +For example given "cat" and {"rectify", "race", "archeology", "racoon"}, it should return "archeology", +because "archeology" is the longest word in the given set that contains "c","a",and "t". */ func TestLongestDictionaryWordContainingKey(t *testing.T) { tests := []struct { diff --git a/strings/longest_substring_test.go b/strings/longest_substring_test.go index 8ba79ba..21f85b5 100644 --- a/strings/longest_substring_test.go +++ b/strings/longest_substring_test.go @@ -7,7 +7,15 @@ TestLongestSubstrings tests solution(s) with the following signature and problem func LongestSubstringOfTwoChars(input string) string -Given a string like "aabbc" return the longest substring of two unique characters like "aabb". +Given a string return the longest substring of two unique characters. + +For example given "aabbc", return "aabb" because it's the longest substring that has two unique +characters "a" and "b". + +Other substrings of "aabc" include: + +* "aabbc", contains more than 2 unique characters. +* "bbc", shorter than "aabb". */ func TestLongestSubstrings(t *testing.T) { tests := []struct { @@ -21,7 +29,7 @@ func TestLongestSubstrings(t *testing.T) { {"aabbc", "aabb"}, {"ada", "ada"}, {"dafff", "afff"}, - {"assdeeeddfffha", "deeedd"}, + {"abbdeeeddfffha", "deeedd"}, } for i, test := range tests { diff --git a/strings/look_and_tell_test.go b/strings/look_and_tell_test.go index bd92557..0472292 100644 --- a/strings/look_and_tell_test.go +++ b/strings/look_and_tell_test.go @@ -7,8 +7,27 @@ TestFindDuplicate tests solution(s) with the following signature and problem des func LookAndTell(depth int) []string -Given a depth, return the output of look and tell an algorithm where each line reads the -last line. For example "1" is read as "11" (one one), and "11" is read as "21" (two ones). +Given a positive integer n, return the output of the Look and Tell algorithm until the nth depth. + +The Look and Tell algorithm starts by outputting 1 at first level, then at each subsequent level +it reads the previous line by counting the number of times a digit is repeated and then writes +the count and the digit. + +For example given 4, return: + +1 +11 +21 +1211 + +Which reads: +one +one one +two ones +one two one one. + +The third output (two ones) reads the level before it which is 11. Two ones means repeat +1 two times i.e. 11. */ func TestFindDuplicate(t *testing.T) { tests := []struct { diff --git a/strings/number_in_english_test.go b/strings/number_in_english_test.go index 475ada1..ed92513 100644 --- a/strings/number_in_english_test.go +++ b/strings/number_in_english_test.go @@ -7,8 +7,12 @@ TestReadNumberInEnglish tests solution(s) with the following signature and probl func NumberInEnglish(num int) string -Given a number like 34, return how the number would be read in English e.g. (Thirty Four) for -integers smaller than one Trillion. +Given n a positive integer smaller than a Trillion, return the number in English words. + +For example given 0, return "Zero". +For example given 34, return "Thirty Four". +For example given 10, return "Ten". +For example given 900000000001, return "Nine Hundred Billion One". */ func TestReadNumberInEnglish(t *testing.T) { tests := []struct { @@ -18,10 +22,12 @@ func TestReadNumberInEnglish(t *testing.T) { {0, "Zero"}, {1, "One"}, {2, "Two"}, + {10, "Ten"}, {34, "Thirty Four"}, {123456789, "One Hundred Twenty Three Million Four Hundred Fifty Six Thousand Seven Hundred Eighty Nine"}, {1000000000, "One Billion"}, {100000000000, "One Hundred Billion"}, + {900000000001, "Nine Hundred Billion One"}, } for i, test := range tests { diff --git a/strings/reverse_vowels_test.go b/strings/reverse_vowels_test.go index 3d1a3df..20e4434 100644 --- a/strings/reverse_vowels_test.go +++ b/strings/reverse_vowels_test.go @@ -7,7 +7,11 @@ TestReverseVowels tests solution(s) with the following signature and problem des func ReverseVowels(str string) (string, error) -Given a string e.g. "coat", reverse the order in which vowels appear e.g. "caot". +Given a string, return the same string while reversing the vowels {"a", "e", "i", "o", "u"} +appear in it. + +For example given "coat", return "caot", because the vowels are "o" and "a" and their positions +are reversed. */ func TestReverseVowels(t *testing.T) { tests := []struct { diff --git a/strings/roman_numerals_test.go b/strings/roman_numerals_test.go index 3e418be..c825d53 100644 --- a/strings/roman_numerals_test.go +++ b/strings/roman_numerals_test.go @@ -7,7 +7,24 @@ TestIntToRoman tests solution(s) with the following signature and problem descri func IntToRoman(input int) string -Given a positive integer like 1917 return the Roman numeral like MCMXVII. +Given a positive integer like return the equivalent inRoman numerals: + +For example: + +* Given 1, return I +* Given 4, return IV +* Given 5, return V +* Given 9, return IX +* Given 10, return X +* Given 40, return XL +* Given 50, return L +* Given 90, return XC +* Given 100, return C +* Given 400, return CD +* Given 500, return D +* Given 900, return CM +* Given 1000, return M +* Given 1917, return MCMXVII. */ func TestIntToRoman(t *testing.T) { tests := []struct {