Skip to content

Commit be26326

Browse files
authored
strings package review
1 parent c985525 commit be26326

9 files changed

+175
-18
lines changed

array/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ To provide a real-world analogy, consider an array of athletes preparing for a s
66

77
## Implementation
88

9-
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.
9+
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.
1010

1111
```Go
1212
package main

strings/README.md

+78-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
# String
22

3-
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.
3+
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.
44

55
## Implementation
66

7-
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:
7+
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.
8+
9+
The `strings` package provides several useful convenience functions. Examples include:
810

911
* [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)
1012
* [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.
1315
* [Title](https://golang.org/pkg/strings/#Title), [ToLower](https://golang.org/pkg/strings/#ToLower), [ToUpper](https://golang.org/pkg/strings/#ToUpper)
1416
* [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)
1517

16-
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.
18+
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.
19+
20+
```Go
21+
package main
22+
23+
import "fmt"
24+
25+
/*
26+
main outputs the rune (int32) value of each character:
27+
28+
Char #0 "a" has value 97
29+
Char #1 "A" has value 65
30+
Char #2 "和" has value 21644
31+
Char #5 "平" has value 24179
32+
Char #8 "😊" has value 128522
33+
*/
34+
func main() {
35+
for i, r := range "aA𓅚😊" {
36+
fmt.Printf("Char #%d %q has value %d\n", i, string(r), r)
37+
}
38+
}
39+
```
40+
41+
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.
1742

1843
```Go
1944
package main
2045

46+
import "fmt"
47+
48+
func main() {
49+
number := 1
50+
value := 1.1
51+
name := "foo"
52+
53+
output := fmt.Sprintf("%d %f %s", number, value, name)
54+
fmt.Println(output) // 1 1.100000 foo
55+
}
56+
```
57+
58+
### Regular Expressions
59+
60+
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.
61+
62+
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`}.
63+
64+
```GO
65+
package main
66+
2167
import (
2268
"fmt"
69+
"regexp"
2370
)
2471

72+
var fishPattern = regexp.MustCompile(`(?i).*fi\w*sh\b`)
73+
74+
// main outputs [selfish][shellfish][fish][finish][Finnish]
2575
func main() {
26-
for i, r := range "abcd" {
27-
fmt.Printf("Char #%d %q has value %d\n", i, string(r), r)
76+
inputs := []string{"shift", "selfish", "shellfish", "fish dish", "finish", "Finnish"}
77+
78+
for _, input := range inputs {
79+
matches := fishPattern.FindAllString(input, -1)
80+
if len(matches) > 0 {
81+
fmt.Print(matches)
82+
}
2883
}
2984
}
3085
```
3186

3287
## Complexity
3388

34-
Strings have the same complexity as [arrays](../array/) and slices in Go.
89+
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.
90+
91+
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.
92+
93+
```GO
94+
package main
95+
96+
import "fmt"
97+
98+
// main Outputs 41 f0 93 85 9a f0 9f 98 8a.
99+
func main() {
100+
input := "A𓅚😊"
101+
for i := 0; i < len(input); i++ {
102+
fmt.Printf("%x ", input[i])
103+
}
104+
}
105+
```
35106

36-
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.
107+
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.
37108

38109
## Application
39110

strings/in_memory_database_test.go

+29
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,34 @@ TestInMemoryDictionary tests solution(s) with the following signature and proble
1212
1313
Write an in memory database that stores string key-value pairs and supports SET, GET, EXISTS,
1414
and UNSET commands. It should also allow transactions with BEGIN, COMMIT and ROLLBACK commands.
15+
16+
For example:
17+
18+
GET A // outputs nil
19+
BEGIN
20+
SET A 1
21+
GET A // outputs 1
22+
COMMIT
23+
GET A // outputs 1
24+
25+
At the first GET A, nil is returned because it has never been set. The second and third
26+
GET A will output 1 because the value of A was set as 1.
27+
28+
BEGIN, ROLLBACK and COMMIT are referred to as transactions in databases. A transaction is
29+
started by BEGIN. The commands that are followed by a BEGIN are completely ignored if a ROLLBACK
30+
command is given and actually applied only when COMMIT command is given.
31+
32+
For example:
33+
34+
BEGIN
35+
SET A 1
36+
COMMIT
37+
BEGIN
38+
SET A 2
39+
ROLLBACK
40+
GET A // outputs 1
41+
42+
The output is 1 because SET A 2 was never committed so the value of A remains 1.
1543
*/
1644
func TestInMemoryDictionary(t *testing.T) {
1745
tests := []struct {
@@ -21,6 +49,7 @@ func TestInMemoryDictionary(t *testing.T) {
2149
{"EXISTS A\nSET A 1\nGET A\nEXISTS A\nUNSET A\nGET A\nEXISTS A", "false 1 true nil false"},
2250
{"GET A\nBEGIN\nSET A 1\nGET A\nROLLBACK\nGET A", "nil 1 nil"},
2351
{"GET A\nBEGIN\nSET A 1\nGET A\nCOMMIT\nGET A", "nil 1 1"},
52+
{"BEGIN\nSET A 1\nCOMMIT\nBEGIN\nSET A 2\nROLLBACK\nGET A", "1"},
2453
{"SET A 1\nGET A\nBEGIN\nSET A 2\nGET A\nBEGIN\nUNSET A\nGET A\nCOMMIT\nROLLBACK\nGET A", "1 2 nil 1"},
2554
{"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"},
2655
}

strings/longest_dictionary_word_test.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ TestLongestDictionaryWordContainingKey tests solution(s) with the following sign
77
88
func LongestDictionaryWordContainingKey(key string, dic []string) string
99
10-
Given a key like "car", and a dictionary like {"rectify", "race", "archeology", "racoon"} return the longest
11-
dictionary word that contains every letter of the key like "archeology".
10+
Given a key as string, and a slice of strings containing a dictionary of words, return the longest
11+
word that contains all letters of the key.
12+
13+
For example given "cat" and {"rectify", "race", "archeology", "racoon"}, it should return "archeology",
14+
because "archeology" is the longest word in the given set that contains "c","a",and "t".
1215
*/
1316
func TestLongestDictionaryWordContainingKey(t *testing.T) {
1417
tests := []struct {

strings/longest_substring_test.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,15 @@ TestLongestSubstrings tests solution(s) with the following signature and problem
77
88
func LongestSubstringOfTwoChars(input string) string
99
10-
Given a string like "aabbc" return the longest substring of two unique characters like "aabb".
10+
Given a string return the longest substring of two unique characters.
11+
12+
For example given "aabbc", return "aabb" because it's the longest substring that has two unique
13+
characters "a" and "b".
14+
15+
Other substrings of "aabc" include:
16+
17+
* "aabbc", contains more than 2 unique characters.
18+
* "bbc", shorter than "aabb".
1119
*/
1220
func TestLongestSubstrings(t *testing.T) {
1321
tests := []struct {
@@ -21,7 +29,7 @@ func TestLongestSubstrings(t *testing.T) {
2129
{"aabbc", "aabb"},
2230
{"ada", "ada"},
2331
{"dafff", "afff"},
24-
{"assdeeeddfffha", "deeedd"},
32+
{"abbdeeeddfffha", "deeedd"},
2533
}
2634

2735
for i, test := range tests {

strings/look_and_tell_test.go

+21-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,27 @@ TestFindDuplicate tests solution(s) with the following signature and problem des
77
88
func LookAndTell(depth int) []string
99
10-
Given a depth, return the output of look and tell an algorithm where each line reads the
11-
last line. For example "1" is read as "11" (one one), and "11" is read as "21" (two ones).
10+
Given a positive integer n, return the output of the Look and Tell algorithm until the nth depth.
11+
12+
The Look and Tell algorithm starts by outputting 1 at first level, then at each subsequent level
13+
it reads the previous line by counting the number of times a digit is repeated and then writes
14+
the count and the digit.
15+
16+
For example given 4, return:
17+
18+
1
19+
11
20+
21
21+
1211
22+
23+
Which reads:
24+
one
25+
one one
26+
two ones
27+
one two one one.
28+
29+
The third output (two ones) reads the level before it which is 11. Two ones means repeat
30+
1 two times i.e. 11.
1231
*/
1332
func TestFindDuplicate(t *testing.T) {
1433
tests := []struct {

strings/number_in_english_test.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@ TestReadNumberInEnglish tests solution(s) with the following signature and probl
77
88
func NumberInEnglish(num int) string
99
10-
Given a number like 34, return how the number would be read in English e.g. (Thirty Four) for
11-
integers smaller than one Trillion.
10+
Given n a positive integer smaller than a Trillion, return the number in English words.
11+
12+
For example given 0, return "Zero".
13+
For example given 34, return "Thirty Four".
14+
For example given 10, return "Ten".
15+
For example given 900000000001, return "Nine Hundred Billion One".
1216
*/
1317
func TestReadNumberInEnglish(t *testing.T) {
1418
tests := []struct {
@@ -18,10 +22,12 @@ func TestReadNumberInEnglish(t *testing.T) {
1822
{0, "Zero"},
1923
{1, "One"},
2024
{2, "Two"},
25+
{10, "Ten"},
2126
{34, "Thirty Four"},
2227
{123456789, "One Hundred Twenty Three Million Four Hundred Fifty Six Thousand Seven Hundred Eighty Nine"},
2328
{1000000000, "One Billion"},
2429
{100000000000, "One Hundred Billion"},
30+
{900000000001, "Nine Hundred Billion One"},
2531
}
2632

2733
for i, test := range tests {

strings/reverse_vowels_test.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ TestReverseVowels tests solution(s) with the following signature and problem des
77
88
func ReverseVowels(str string) (string, error)
99
10-
Given a string e.g. "coat", reverse the order in which vowels appear e.g. "caot".
10+
Given a string, return the same string while reversing the vowels {"a", "e", "i", "o", "u"}
11+
appear in it.
12+
13+
For example given "coat", return "caot", because the vowels are "o" and "a" and their positions
14+
are reversed.
1115
*/
1216
func TestReverseVowels(t *testing.T) {
1317
tests := []struct {

strings/roman_numerals_test.go

+18-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,24 @@ TestIntToRoman tests solution(s) with the following signature and problem descri
77
88
func IntToRoman(input int) string
99
10-
Given a positive integer like 1917 return the Roman numeral like MCMXVII.
10+
Given a positive integer like return the equivalent inRoman numerals:
11+
12+
For example:
13+
14+
* Given 1, return I
15+
* Given 4, return IV
16+
* Given 5, return V
17+
* Given 9, return IX
18+
* Given 10, return X
19+
* Given 40, return XL
20+
* Given 50, return L
21+
* Given 90, return XC
22+
* Given 100, return C
23+
* Given 400, return CD
24+
* Given 500, return D
25+
* Given 900, return CM
26+
* Given 1000, return M
27+
* Given 1917, return MCMXVII.
1128
*/
1229
func TestIntToRoman(t *testing.T) {
1330
tests := []struct {

0 commit comments

Comments
 (0)