Skip to content

Instantly share code, notes, and snippets.

@parrotbait
Created January 17, 2019 11:16
Show Gist options
  • Save parrotbait/78bb0bd17eb86355132d85449605084b to your computer and use it in GitHub Desktop.
Save parrotbait/78bb0bd17eb86355132d85449605084b to your computer and use it in GitHub Desktop.
Algorithm to determine if a string has all unique characters. What if you cannot use any additional data structures?
import UIKit
// This code is based on implementing the following:
// Implement an algorithm to determine if a string has all unique characters. What if you cannot use any additional data structures?
extension Character {
func toString() -> String {
return String(self)
}
}
// Runs at O(n) due to O(1) set lookup (and insertion) but still need to potentially iterate through all N elements. This cannot be avoided.
// Insertion and lookup of sets can degrade to O(n) if there are many hashing clashes and end up iterating through the 'buckets' but in this case there shouldn't be as it's individual characters
// This algorithm uses potentially N extra elements stored in the Set, if string has unique characters
func hasUniqueCharactersWithDataStructure(_ toCheck: String) -> Bool {
var existingChars = Set<String>()
for char in toCheck {
let charAsString = char.toString()
if existingChars.contains(charAsString) {
return false
}
existingChars.insert(charAsString)
}
return true
}
// Should operate at O(nlog(n) + n) which is O(nlog(n)) in the end
// In terms of size, it uses an extra string of size N which is sorted
func hasUniqueCharactersWithoutDataStructure(_ toCheck: String) -> Bool {
var sortedStr = toCheck.sorted()
var index = sortedStr.startIndex
for _ in 0..<sortedStr.count-1 {
let indexNext = sortedStr.index(index, offsetBy: 1)
if sortedStr[index] == sortedStr[indexNext] {
return false
}
index = indexNext
}
return true
}
// This is the worst algorithm, operating at O(nΒ²)
// It all happens in place so it the most efficient in terms of space
func hasUniqueCharactersInPlace(_ toCheck: String) -> Bool {
var index = toCheck.startIndex
for _ in 0..<toCheck.count-1 {
var indexNext = toCheck.index(index, offsetBy: 1)
while indexNext != toCheck.endIndex {
if toCheck[index] == toCheck[indexNext] {
return false
}
indexNext = toCheck.index(indexNext, offsetBy: 1)
}
index = toCheck.index(index, offsetBy: 1)
}
return true
}
func iterate(_ uniquefunc: (_: String)->Bool, _ toCheck: String) -> (Bool, Double) {
let t1 = CACurrentMediaTime()
let iterations = 1000
var result = false
for i in 0..<iterations {
result = uniquefunc(toCheck)
}
return (result, CACurrentMediaTime() - t1)
}
func checkUniqueCharacters(_ toCheck: String) {
print ("Checking '\(toCheck)':")
if toCheck.isEmpty {
return
}
if toCheck.count == 1 {
return
}
var result = iterate(hasUniqueCharactersWithDataStructure, toCheck)
print ("Extra dataset (Set), result: \(result.0) - duration: \(result.1)s")
result = iterate(hasUniqueCharactersWithoutDataStructure, toCheck)
print ("Sorted, result: \(result.0) - duration: \(result.1)s")
result = iterate(hasUniqueCharactersInPlace, toCheck)
print ("In place, result: \(result.0) - duration: \(result.1)s")
}
var unique1 = "abcdefg"
checkUniqueCharacters(unique1)
var unique2 = "a 34vsdjy"
checkUniqueCharacters(unique2)
var unique3 = "a"
checkUniqueCharacters(unique3)
var notunique1 = "aa"
checkUniqueCharacters(notunique1)
var notunique2 = ""
checkUniqueCharacters(notunique2)
var notunique3 = "abcdefghijkal"
checkUniqueCharacters(notunique3)
var emojirepeat = "πŸ‘πŸ˜€πŸš€πŸ‘πŸ˜€πŸš€"
checkUniqueCharacters(emojirepeat)
var emojinorepeat = "πŸ‘πŸ˜€πŸš€"
checkUniqueCharacters(emojinorepeat)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment