Last active
July 26, 2024 19:26
-
-
Save dautermann/059fd340622b62feeb0241813f900142 to your computer and use it in GitHub Desktop.
Caesar Cipher Message Match
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Question: | |
//Given a list of strings, group together strings that decode to the same message, using Caesar cipher encryption. | |
// | |
//Ex: | |
//input: ['abc', 'bcd', 'bde', 'dfg', 'hjk'] | |
//output: [['abc', 'bcd'], ['bde', 'dfg', 'hjk']] | |
// | |
//A Caesar cipher is an encryption algorithm where rotation is applied to every character in the message. Shift value can be any number, but must be consistent across the message | |
// | |
//Algorithm: | |
// | |
//a rot(1) b | |
//a rot(2) c | |
//a rot(3) d | |
// | |
// 1 3 19 2 | |
//'abc' <==> 'bcd' <==> 'efg' <==> 'xyz' <==> 'zab' | |
// Answer | |
import Foundation | |
// from https://stackoverflow.com/questions/24092884/get-nth-character-of-a-string-in-swift | |
extension String { | |
subscript(idx: Int) -> String { | |
String(self[index(startIndex, offsetBy: idx)]) | |
} | |
} | |
// https://www.dotnetperls.com/caesar-swift | |
func caesar(value: String, shift: Int) -> String { | |
// Empty character array. | |
var result = [Character]() | |
// Loop over utf8 values. | |
for u in value.utf8 { | |
// Apply shift to UInt8. | |
let s = Int(u) + shift | |
// See if value exceeds Z. | |
// ... The Z is 26 past "A" which is 97. | |
// ... If greater than "Z," shift backwards 26. | |
// ... If less than "A," shift forward 26. | |
if s > 97 + 25 { | |
result.append(Character(UnicodeScalar(s - 26)!)) | |
} else if s < 97 { | |
result.append(Character(UnicodeScalar(s + 26)!)) | |
} else { | |
result.append(Character(UnicodeScalar(s)!)) | |
} | |
} | |
// Return String from array. | |
return String(result) | |
} | |
// from https://stackoverflow.com/questions/26728477/how-can-i-combine-two-dictionary-instances-in-swift | |
extension Dictionary { | |
static func +=(lhs: inout Self, rhs: Self) { | |
lhs.merge(rhs) { _ , new in new } | |
} | |
static func +=<S: Sequence>(lhs: inout Self, rhs: S) where S.Element == (Key, Value) { | |
lhs.merge(rhs) { _ , new in new } | |
} | |
} | |
// https://stackoverflow.com/questions/29835242/whats-the-simplest-way-to-convert-from-a-single-character-string-to-an-ascii-va | |
extension StringProtocol { | |
var asciiValues: [UInt8] { compactMap(\.asciiValue) } | |
} | |
func findMatchesWithinThis(array: [String]) -> [String:[String]]? { | |
// probably won't happen but just in case | |
if (array.count == 0) { | |
return nil | |
} | |
var matchesDictionary: [String:[String]] = [array[0]:[array[0]]] | |
array.enumerated().forEach { (index, eachWordA) in | |
// Swift.print("checking word \(eachWordA)") | |
if index > 0 { | |
var foundMatch = false | |
for eachMatch in matchesDictionary.keys { | |
let aChar = eachWordA[0].asciiValues[0] | |
let mChar = eachMatch[0].asciiValues[0] | |
// Swift.print("aChar is \(aChar) \(eachWordA[0]) & mChar is \(mChar) \(eachMatch[0])") | |
var diff: Int = 0 | |
if (aChar > mChar) { | |
diff = Int(aChar - mChar) * -1 | |
} else { | |
diff = Int(mChar - aChar) | |
} | |
// Swift.print("checking \(eachWordA) is \(diff)") | |
// Swift.print("diff between \(eachWordA[0]) from \(eachWordA) & \(eachMatch[0]) from \(eachMatch) is \(diff)") | |
let rotatedWord = caesar(value: eachWordA, shift: diff) | |
if rotatedWord == eachMatch { | |
// Swift.print("rotated word is \(rotatedWord) \(rotatedWord.count) matches \(eachMatch) , appending \(eachWordA)") | |
matchesDictionary[eachMatch]?.append(eachWordA) | |
foundMatch = true | |
} | |
} | |
if foundMatch == false { | |
// Swift.print("creating new entry for \(eachWordA)") | |
matchesDictionary[eachWordA] = [eachWordA] | |
} | |
} | |
} | |
// Swift.print("matchesDictionary is \(matchesDictionary)") | |
return matchesDictionary | |
} | |
func findStringsThatDecodeToSameMessage(_ param: String) -> String { | |
// split the input string into a list of strings | |
let arrayOfStrings = param.components(separatedBy: " ") | |
// come up with dictionary where key is length, | |
// value is an array of strings matching that length | |
var dictOfStrings: [Int:[String]] = [Int:[String]]() | |
for eachString in arrayOfStrings { | |
let length = eachString.count | |
if let _ = dictOfStrings[length] { | |
dictOfStrings[length]?.append(eachString) | |
} else { | |
dictOfStrings[length] = [eachString] | |
} | |
} | |
//[abc] = [abc, bcd] | |
// abc, bcd : (b-a = 1) : bcd => -1 = abc | |
var totalResults: [String:[String]] = [String:[String]]() | |
for eachArray in dictOfStrings.values { | |
// Swift.print("checking \(eachArray); eachArray count is \(eachArray.count)") | |
if let results = findMatchesWithinThis(array: eachArray) { | |
totalResults += results | |
} | |
} | |
var outputString: String = "" | |
// then finish by taking those matches and appending them to an output string | |
for eachValue in totalResults.values { | |
outputString.append("[") | |
outputString.append(contentsOf: eachValue.joined(separator: " ")) | |
outputString.append("],") | |
} | |
return outputString | |
} | |
Swift.print(findStringsThatDecodeToSameMessage("abc bcd bde dfg hjk")) | |
Swift.print(findStringsThatDecodeToSameMessage("xyz zab bde dfg hjk")) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment