LeetCode 249. Group Shifted Strings - swift

I am solving the leetcode question 249. Group Shifted Strings. Can someone please explain how the keys are stored in the hashMap?
We can shift a string by shifting each of its letters to its successive letter.
For example, "abc" can be shifted to be "bcd".
We can keep shifting the string to form a sequence.
For example, we can keep shifting "abc" to form the sequence: "abc" -> "bcd" -> ... -> "xyz".
Given an array of strings strings, group all strings[i] that belong to the same shifting sequence. You may return the answer in any order.
Input: strings = ["abc","bcd","acef","xyz","az","ba","a","z"]
Output: [["acef"],["a","z"],["abc","bcd","xyz"],["az","ba"]]
func groupStrings(_ strings: [String]) -> [[String]] {
var dict = [[Int]: [String]]()
for word in strings {
var key = [0]
if word.count > 1 {
var a = Array(word)
let pivot = Int(a[0].asciiValue!) - 97
for i in 1..<a.count {
let index = (Int(a[i].asciiValue!) - 97 + 26 - pivot) % 26
key.append(index)
}
}
if var array = dict[key] {
array.append(word)
dict[key] = array
} else {
dict[key] = [word]
}
}
return dict.keys.compactMap { dict[$0] }
}
"[[0, 2, 4, 5]: ["acef"], [0, 25]: ["az", "ba"], [0]: ["a", "z"], [0, 1, 2]: ["abc", "bcd", "xyz"]]"

Your question is not much clear but I believe u are requesting an explanation to the groupStrings function.
In your question u have to find the shifting sequences. eg : abc -> bcd -> cde ..... -> xyz.
So the logic behind this is checking the ASCII values of each character. Because if two strings form a shifting sequence the differences between the ASCII values of any consecutive pair of their characters are the same.
For an example we know abc bcd xyz are in a shifting sequence`.
Characters ASCCI values Difference from the 1st value
a b c 97 98 99. 0 1 2
b c d 98 99 100. 0 1 2
x y z 120 121 122. 0 1 2
Basically what happening in your function is creating a dictionary which contains those differences as keys and strings as values.
func groupStrings(_ strings: [String]) -> [[String]] {
var dict = [[Int]: [String]]() // initialize the dictionary
for word in strings {
var key = [0]
if word.count > 1 {
var a = Array(word) //create an array from the charachters of the word
let pivot = Int(a[0].asciiValue!) - 97 // get the ASCII value of the 1st lestter
// we substract 97 because for english letters ASCII values start from 97.
// by substracting 97 we can map the ASCCII of a to 0, b to 1 .... z to 26
for i in 1..<a.count {
// calulate the differences between the ASCII values of any consecutive pairs
let index = (Int(a[i].asciiValue!) - 97 + 26 - pivot) % 26
key.append(index)
}
}
// here check if there is an already a difference sequence in the dictionary
//if true we append the word to that exsiting key value pair
//else create a new key value pair
if var array = dict[key] {
array.append(word)
dict[key] = array
} else {
dict[key] = [word]
}
}
return dict.keys.compactMap { dict[$0] }
}

Related

Is there a simple way to merge values based on keys in Swift dictionaries?

I have a text divided into an array of strings where the user can tap on each word, adding the word's index (key) and the string (value) to a dictionary.
Now, if the user adds two or more words that are adjacent, I would like the concatenate the string and make them share one index.
My idea was to use a computed property that rearranges the array of strings based on the keys and values in the dictionary. So when the user taps on a word, the function should update the dictionary while checking if there are any adjacent indexes already added.
Example code:
let text = "This is a test for merging adjacent words that the user has selected."
//The text divided in separate words that can be tapped
var arrayOfString: [String] {
text.components(separatedBy: " ")
}
//If a user taps on a word it will be saved with its index
var userSelectedWords: [Int:String] = [2 : "a", 3 : "test", 4 : "for", 6 : "adjacent", 7 : "words", 9 : "the", 11 : "has"]
//Mapping all the keys into an array
var selectedKeys = userSelectedWords.map { $0.key }.sorted()
var indexToRemove = [Int]()
for i in 0..<selectedKeys - 1 {
//If the key has a value of one less that the succeding key, the words are adjacent
if selectedKeys[i] == selectedKeys[i + 1] - 1 {
indexToRemove.append(selectedKeys[i+1])
if let currentWord = userSelectedWords[selectedKeys[i]], let nextWord = userSelectedWords[selectedKeys[i + 1]] {
concatenatedString.append("\(currentWord) \(nextWord)")
}
}
}
print(indexToRemove)
//Prints: [3, 4, 7] which are the indexes that should be removed.
print(concatenatedString)
//Prints: ["a test", "test for", "adjacent words"]
/*
Here I'm stuck. If there are more than two words adjacent, the function will of
course continue the iteration and create a new item in the concatenatedString.
It feels like it starts to get way too complicated.
*/
I'd very much appreciate any input or help in this regard. Maybe I'm just looking at it the wrong way...
A functional approach would be to create an array of Range objects based upon the contiguous sorted keys, and then filter for those whose length was greater than 1
let text = "This is a test for merging adjacent words that the user has selected."
let allWords = text.components(separatedBy: " ")
let userSelectedWords = [2 : "a", 3 : "test", 4 : "for", 6 : "adjacent", 7 : "words", 9 : "the", 11 : "has"]
let result = userSelectedWords.keys
.sorted()
.reduce(into: [Range]()) { ranges, index in
if let range = ranges.last, range.endIndex == index {
ranges[ranges.count - 1] = range.startIndex ..< index + 1
} else {
ranges.append(index ..< index + 1)
}
}
.filter { $0.count > 1 }
.map { allWords[$0].joined(separator: " ") }
print(result) // ["a test for", "adjacent words"]

Recursive call in swift

I am confused with the following recursive example. The place where recursion happens, the local variable needs to be updated every time. I wonder then how it could store the base result? And let variable is not mutable, how it updates?
The question is as follows for the following solution:
Implement a recursive function named digits that takes a positive
integer number and return an array containing it’s digits in order.
Function call:
digits(123)
Function output:
[1, 2, 3]
func digits(_ number:Int) -> [Int]
{
if number >= 10 {
// confusion in the following line
let firstDigits = digits(number / 10)
let lastDigit = number % 10
return firstDigits + [lastDigit]
} else {
return [number]
}
}
I would rather approach the problems as follows. I wonder what is the advantages of having the above solution.
func digits(_ number:Int) -> [Int]
{
if number >= 10 {
let lastDigit = number % 10
return digits(number / 10) + [lastDigit]
} else {
return [number]
}
}
I wonder then how it could store the base result? And let variable is not mutable, how it updates?
firstDigits never changes, it is only set once in each invocation of digits. Each invocation of digits has it's own variables.
Example Execution
In the following example I show how the execute proceeds as a series of substitutions.
digits(123) ->
digits(123 / 10) + [123 % 10] ->
digits(12) + [3] ->
digits(12 / 10) + [12 % 10] + [3] ->
digits(1) + [2] + [3] ->
[1] + [2] + [3] ->
[1, 2, 3]
Another way to write it that may be more clear
func digits(_ number:Int) -> [Int]
{
if number >= 10 {
return digits(number / 10) + [number % 10]
} else {
return [number]
}
}

Add every 100 string keys from dictionary in array of strings with comma separator

So I have a dictionary with 450 or sometimes 1313 string keys and I want to add all keys in array of strings, so earch string has to contains from 1 to 100 keys it depends how big is the dictionary.
Example if there are 450 keys:
let array = ["first 100 keys here comma separated","second 100 keys here comma separated","third 100 keys here comma separated","fourth 100 keys here comma separated","and last 50 keys comma separated"]
You just need to group your array elements and use map to join your keys using joined(separator: ", "):
extension Array {
func group(of n: IndexDistance) -> Array<Array> {
return stride(from: 0, to: count, by: n)
.map { Array(self[$0..<Swift.min($0+n, count)]) }
}
}
Testing:
let dic = ["f":1,"a":1,"b":1,"c":1,"d":1,"e":1, "g": 1]
let arr = Array(dic.keys).group(of: 2).map{
$0.joined(separator: ", ")
}
arr //["b, a", "c, f", "e, g", "d"]

Swift 3 : Negative Int to hexadecimal

Hy everyone,
I need to transform a Int to its hexadecimal value.
Example : -40 => D8
I have a working method for positive (or unsigned) Int but it doesn't work as expected with negatives. Here's my code.
class func encodeHex(data:[Int]) -> String {
let hexadecimal = data.reduce("") { (string , element) in
var append = String(element, radix:16 , uppercase : false)
if append.characters.count == 1 {
append = "0" + append
}
return string + append
}
return hexadecimal
}
If I pass -40 I get -28.
Can anyone help ? Thanks :)
I assume from your existing code that all integers are in the range
-128 ... 127. Then this would work:
func encodeHex(data:[Int]) -> String {
return data.map { String(format: "%02hhX", $0) }.joined()
}
The "%02hhX" format prints the least significant byte of the
given integer in base 16 with 2 digits.
Example:
print(encodeHex(data: [40, -40, 127, -128]))
// 28D87F80
D8 is the last byte of binary representation of -40. The remaining three bytes are all FFs.
If you are looking for a string that represents only the last byte, you can obtain by first converting your number to unsigned 8-bit integer, and then converting it to hex, like this:
let x = UInt8(bitPattern:Int8(data))
let res = String(format:"%02X", x)

map function in Swift converting String to Int?

let digitNames = [
0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]
let strings = numbers.map {
(var number) -> String in
var output = ""
while number > 0 {
output = digitNames[number % 10]! + output
number /= 10
}
return output
}
// strings is inferred to be of type [String]
// its value is ["OneSix", "FiveEight", "FiveOneZero"]
I was hoping if there is anyone that could explain how this code works (it is taken from apple's developer page for swift under "closures). I'm not too sure especially what the code in the "while" loop means :/ how exactly is the number converted to string?
map function is Higher Order Level function and it is used to do some operation on single elements of array and return a transformed array generated after your operations.
numbers.map will traverse each element of array and transform the elements by doing some operation and returned a transformed array .
output = digitNames[number % 10]! + output
1) for first element in array 16 in first iteration of while loop number % 10 will return 6 as a reminder of 16 after dividing by 10 so digitName[6] will assign output to Six
let strings = numbers.map {
(var number) -> String in
var output = ""
while number > 0 {
output = digitNames[number % 10]! + output //16
number /= 10
}
return output
}
2) It divides number by 10 and which will give 1 now number will be 1
3) while number > 0 { checks if number is greater than 0 yes it is 1
4) Again iterate now this time digitNames[number % 10]! return One and by appending previous output it will become One append output(which is Six).So OneSix
Your first element become OneSix.This will done for each element and after all elements map return String array.So finally String become
["OneSix", "FiveEight", "FiveOneZero"]