Lesson 2
String Transformation and Frequency Analysis with Alice
Introduction

Hello! Are you ready for an exciting voyage into the wonderful realm of strings and data structures? Today, we will assist Alice, an aspiring cryptographer, with an intriguing string manipulation task. She loves playing with strings and has come up with a unique string-encoding scheme. I assure you, this will be an enlightening journey that will stretch your programming muscles. Let's get started!

Task Statement

Alice has devised a unique way of encoding words. She takes a word and replaces each character with the next character in the alphabetical order. In other words, given a string word, for each character, if it's not z, she replaces it with the character that comes next alphabetically. For the character z, she replaces it with a.

Another element of Alice's algorithm involves frequency analysis. After shifting the characters, she counts the frequency of each character in the new string. Then, she creates an association of each character with its frequency and ASCII value. Each character maps to a number, which is a product of the ASCII value of the character and its frequency. Our task is to construct a slice containing these products, sorted in descending order.

Example: for the input string "banana", the output should be [294, 222, 99].

The string "banana" will be shifted to "cbobob".

Calculate the product of frequency and ASCII value for each character:

  • The ASCII value for c is 99; it appears once in the string, so its product is 991=9999 \cdot 1 = 99.
  • The ASCII value for b is 98; it appears three times in the string, so its product is 983=29498 \cdot 3 = 294.
  • The ASCII value for o is 111; it appears twice in the string, so its product is 1112=222111 \cdot 2 = 222.

Collecting these products into a slice gives [99, 294, 222]. Sorting this slice in descending order results in [294, 222, 99].

Step 1 - Mapping characters

Our first step involves mapping each character of the input string to the next alphabetical character. For this, we define the nextString as an empty slice of runes, storing the shift operation result. We then iterate over each character of the input string. If a character is not z, we replace it with the next alphabetical character. If it is z, we replace it with a.

Was that clear? Look at the following function that implements this exact behaviour:

Go
1func characterFrequencyEncoding(word string) string { 2 nextString := []rune{} 3 for _, letter := range word { 4 if letter == 'z' { 5 nextString = append(nextString, 'a') 6 } else { 7 nextString = append(nextString, letter+1) 8 } 9 } 10 return string(nextString) 11}
Step 2 - Counting frequency of characters

The next step is to track the frequency of each character in nextString. We start by initializing an empty map with rune keys and int values, frequencyMap. Then, we iterate over nextString. If the current character exists in frequencyMap, we increment its frequency by 1. If it doesn't exist, we add it to frequencyMap with a frequency of 1. The function that follows exemplifies this functionality:

Go
1func countFrequency(nextString string) map[rune]int { 2 frequencyMap := make(map[rune]int) 3 for _, letter := range nextString { 4 frequencyMap[letter]++ 5 } 6 return frequencyMap 7}
Step 3 - Building the product list

Next, we calculate the numerical representation for each unique character. We initialize an empty slice of integers, combinedValues, to store these numbers. For each character in frequencyMap, we calculate the product of its ASCII representation and its frequency in nextString and append this to combinedValues.

Here's how this requirement may be implemented in code:

Go
1func buildProductList(frequencyMap map[rune]int) []int { 2 combinedValues := []int{} 3 for char, freq := range frequencyMap { 4 combinedValues = append(combinedValues, int(char)*freq) 5 } 6 return combinedValues 7}
Step 4 - Sorting the final values

The final step is to sort the slice combinedValues in descending order. In Go, you can use the sort package to accomplish this. Specifically, sort.Slice allows custom sorting logic by providing a sorting function to determine the order. This sorting function is given the indexes of two elements to compare, i and j. For example, given a values slice, by checking values[i] > values[j], we ensure that if the value at index i is greater than the value at index j, it will be placed before it. This effectively sorts the slice in descending order, as larger values are prioritized to appear earlier in the list.

Here's the sorting step implemented in code:

Go
1func sortDescending(values []int) { 2 sort.Slice(values, func(i, j int) bool { 3 return values[i] > values[j] 4 }) 5}
Complete Solution

Finally, we just need to combine all the previous steps into one function. Here's our complete function:

Go
1package main 2 3import ( 4 "fmt" 5 "sort" 6) 7 8func characterFrequencyEncoding(word string) []int { 9 // Step 1: Mapping each character to the next alphabetical character 10 nextString := []rune{} 11 for _, letter := range word { 12 if letter == 'z' { 13 nextString = append(nextString, 'a') 14 } else { 15 nextString = append(nextString, letter+1) 16 } 17 } 18 19 // Step 2: Counting the frequency of characters in nextString 20 frequencyMap := make(map[rune]int) 21 for _, letter := range nextString { 22 frequencyMap[letter]++ 23 } 24 25 // Step 3: Building the product list 26 combinedValues := []int{} 27 for char, freq := range frequencyMap { 28 combinedValues = append(combinedValues, int(char)*freq) 29 } 30 31 // Step 4: Sorting the final values in descending order 32 sort.Slice(combinedValues, func(i, j int) bool { 33 return combinedValues[i] > combinedValues[j] 34 }) 35 36 // Return the sorted list 37 return combinedValues 38} 39 40// Example 41func main() { 42 word := "banana" 43 result := characterFrequencyEncoding(word) 44 fmt.Println(result) 45}
Lesson Summary

Well done! You've successfully tackled an intricate problem that required you to exercise multiple topics such as string manipulation, map processing, and slice sorting. This task underscored the importance of reusing already calculated values. I encourage you to apply what you've learned today to other tasks. Many more exciting challenges are waiting for you in the upcoming practice sessions. Happy coding!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.