Task from the interview. How we would solve it? - swift

Convert String in this way
let initialString = "atttbcdddd"
// result must be like this "at3bcd4"
But repetition must be more than 2. For example, if we have "aa" the result will be "aa", but if we have "aaa", the result will be "a3"
One more example:
let str = "aahhhgggg"
//result "aah3g4"
My try:
func encrypt(_ str: String) -> String {
let char = str.components(separatedBy: "t") //must input the character
var count = char.count - 1
var string = ""
string.append("t\(count)")
return string
}
if i input "ttttt" it will return "t5" but i should input the character

What you are looking for is the “Run-length encoding”. Note that this is not an encryption!
Here is a possible implementation (explanations inline):
func runLengthEncode(_ str: String) -> String {
var result = ""
var pos = str.startIndex // Start index of current run
while pos != str.endIndex {
let char = str[pos]
// Find index of next run (or `endIndex` if there is none):
let next = str[pos...].firstIndex(where: { $0 != char }) ?? str.endIndex
// Compute the length of the current run:
let length = str.distance(from: pos, to: next)
// Append compressed output to the result:
result.append(length <= 2 ? String(repeating: char, count: length) : "\(char)\(length)")
pos = next // ... and continue with next run
}
return result
}
Examples:
print(runLengthEncode("atttbcdddd")) // at3bcd4
print(runLengthEncode("aahhhgggg")) // aah3g4
print(runLengthEncode("abbbaaa")) // ab3a3

Checkout this :
func convertString(_ input : String) -> String {
let allElements = Array(input)
let uniqueElements = Array(NSOrderedSet(array: allElements)) as! [Character]
var outputString = ""
for uniqueChar in uniqueElements {
var count = 0
for char in allElements {
if char == uniqueChar {
count+=1
}
}
if count > 2 {
outputString += "\(uniqueChar)\(count)"
} else if count == 2 {
outputString += "\(uniqueChar)\(uniqueChar)"
} else {
outputString += "\(uniqueChar)"
}
}
return outputString
}
Input : convertString("atttbcdddd")
Output : at3bcd4

I've tried it before for one of the interview and also I think you too :). However, very simple way to do it is just go through step by step of code.
let initialString = "atttbcdddd"
var previousChar: Character = " "
var output = ""
var i = 1 // Used to count the repeated charaters
var counter = 0 // To check the last character has been reached
//Going through each character
for char in initialString {
//Increase the characters counter to check the last element has been reached. If it is, add the character to output.
counter += 1
if previousChar == char { i += 1 }
else {
output = output + (i == 1 ? "\(previousChar)" : "\(previousChar)\(i)")
i = 1
}
if initialString.count == counter {
output = output + (i == 1 ? "\(previousChar)" : "\(previousChar)\(i)")
}
previousChar = char
}
let finalOutput = output.trimmingCharacters(in: .whitespacesAndNewlines)
print(finalOutput)

let initialString = "atttbcdddd"
let myInitialString = initialString + " "
var currentLetter: Character = " "
var currentCount = 1
var answer = ""
for (_, char) in myInitialString.enumerated(){
if char == currentLetter {
currentCount += 1
} else {
if currentCount > 1 {
answer += String(currentCount)
}
answer += String(char)
currentCount = 1
currentLetter = char
}
}
print(answer)

Use reduce here.
func exp(_ s : String, _ term: String) -> String{ //term_inator: Any Char not in the Sequence.
guard let first = s.first else {return ""}
return """
\(s.dropFirst().appending(term).reduce(("\(first)",1)){ r, c in
let t = c == r.0.last!
let tc = t ? r.1 : 0
let tb = t ? "" : "\(c)"
let ta = t ? "" : r.1 > 2 ? "\(r.1)" : r.1 == 2 ? "\(r.0.last!)" : ""
return (r.0 + ta + tb, tc + 1)
}.0.dropLast())
"""}
print(exp(initialString, " "))
let initialString = "abbbaaa" // ab3a3
let initialString = "aahhhgggg" // aah3g4
let initialString = "aabbaa" // aabbaa

Related

Minimum Window Substring in Swift exceeds LeetCode runtime benchmark

This is a popular question on LeetCode:
Given two strings s and t of lengths m and n respectively,
return the minimum window substring of s such that every character in t (including duplicates)
is included in the window. If there is no such substring, return the empty string "".
The testcases will be generated such that the answer is unique.
A substring is a contiguous sequence of characters within the string.
Example:
Input: s = "ADOBECODEBANC", t = "ABC"
Output: "BANC"
Explanation: The minimum window substring "BANC" includes 'A', 'B', and 'C' from string t.
I converted the java solution provided by LeetCode to Swift since this is the language I am practicing in. Here is my code below:
func minWindowSlidingWindow(_ s: String, _ t: String) -> String
{
if s == t
{
return s
}
var uniqueCharacterHashTable: [Character: Int] = [:]
for character in t
{
if let countOfChar = uniqueCharacterHashTable[character]
{
uniqueCharacterHashTable[character] = countOfChar + 1
continue
}
uniqueCharacterHashTable[character] = 1
}
let uniqueCharactersRequired = uniqueCharacterHashTable.keys.count
var uniqueCharactersFormed = 0
var currentWindowCharacterHashTable: [Character: Int] = [:]
var minSequenceSize = Int.max
var minimumSequenceStart = 0
var minimumSequenceEnd = 0
var currentStartIndexInt = 0
var currentEndIndexInt = 0
while currentEndIndexInt < s.count
{
let endIndex = s.index(s.startIndex, offsetBy: currentEndIndexInt)
var currentCharacter = s[endIndex]
if var characterCount = currentWindowCharacterHashTable[currentCharacter]
{
characterCount += 1
currentWindowCharacterHashTable[currentCharacter] = characterCount
}
else
{
currentWindowCharacterHashTable[currentCharacter] = 1
}
if let _ = uniqueCharacterHashTable[currentCharacter],
currentWindowCharacterHashTable[currentCharacter] == uniqueCharacterHashTable[currentCharacter]
{
uniqueCharactersFormed += 1
}
while currentStartIndexInt <= currentEndIndexInt && uniqueCharactersFormed == uniqueCharactersRequired
{
let startIndex = s.index(s.startIndex, offsetBy: currentStartIndexInt)
currentCharacter = s[startIndex]
if minSequenceSize == Int.max || currentEndIndexInt - currentStartIndexInt + 1 < minSequenceSize
{
minSequenceSize = currentEndIndexInt - currentStartIndexInt + 1
minimumSequenceStart = currentStartIndexInt
minimumSequenceEnd = currentEndIndexInt
}
if let characterCountInWindow = currentWindowCharacterHashTable[currentCharacter]
{
currentWindowCharacterHashTable[currentCharacter] = characterCountInWindow - 1
}
if let _ = uniqueCharacterHashTable[currentCharacter],
let currentCharOriginalCount = uniqueCharacterHashTable[currentCharacter],
let charInWindowCount = currentWindowCharacterHashTable[currentCharacter],
currentCharOriginalCount > charInWindowCount
{
uniqueCharactersFormed -= 1
}
currentStartIndexInt += 1
}
currentEndIndexInt += 1
}
if minSequenceSize == Int.max
{
return ""
}
let startIndex = s.index(s.startIndex, offsetBy: minimumSequenceStart)
let endIndex = s.index(s.startIndex, offsetBy: minimumSequenceEnd)
return String(s[startIndex ... endIndex])
}
This works for the basic test cases but as the string size gets huge like 100,000 for example - it gets super slow even though I use the same data structures (I think) as suggested in the Java solution.
Can anyone point me as to where the bottleneck in this code lies and how could I optimize this further.

How to pick First and Last number from String Swift

i want to get First and Last number from dynamic string.
String could be any number like below.
"1-3,28-30,55" ==> Output is 1 & 55
"104-105,131-132,157" ==> Output is 104 & 157
"188,192,194" ==> Output is 188 & 194
"202" ==> Output is 202 & 0
"204-206,208-212,215-220" Output is 204 & 220
OBJECT
class PalletRangeObject: NSObject {
var start: Int = 0
var end: Int = 0
var strRange: String = ""
init(start: Int, end: Int, range: String) {
self.start = start
self.end = end
self.strRange = range
}
}
For above achievement i have tried below code but in some cases it's not working.
for i in 0..<self.arrPalletRange.count {
let objPlRange = self.arrPalletRange[i]
if !objPlRange.strRange.isEmpty {
var nStart = 0
var nEnd = 0
let pointsArr = objPlRange.strRange.components(separatedBy: ",")
for i in 0..<pointsArr.count {
let arr = pointsArr[i].components(separatedBy: "-")
let newData = arr.map { Int($0)!}
if newData.count == 1 {
if nStart == 0 {
nStart = Int(newData.first ?? 0)
continue
}
nEnd = Int(newData.first ?? 0)
continue
}
else {
if nStart == 0 {
nStart = Int(newData.first ?? 0)
continue
}
nEnd = Int(newData.last ?? 0)
}
}
objPlRange.start = nStart
objPlRange.end = nEnd
}
}
Can anyone please guide me to achieve this.
Thanks in advance
It doesn't have to be that complicated. You seem to just want to get the first and last substring separated by either , or -.
You can use components(separatedBy:)
for i in 0..<self.arrPalletRange.count {
let objPlRange = self.arrPalletRange[i]
let allComponents = objPlRange.strRange.components(separatedBy: CharacterSet([",", "-"]))
if let firstComponent = allComponents.first,
let firstComponentInt = Int(firstComponent),
let lastComponent = allComponents.last,
let lastComponentInt = Int(lastComponentInt) {
objPlRange.start = firstComponentInt
objPlRange.end = lastComponentInt
} else {
// the string is empty, or the values are not valid integers
}
}
Alternatively, you can find the first and last index of a - or ,, and cut the string at those positions. This avoids creating the array of all components, since you don't need most of them.
for i in 0..<self.arrPalletRange.count {
let objPlRange = self.arrPalletRange[i]
let firstIndex = objPlRange.strRange.firstIndex(where: { $0 == "-" || $0 == "," }) ?? objPlRange.strRange.endIndex
let lastIndex = objPlRange.strRange.lastIndex(where: { $0 == "-" || $0 == "," }) ?? objPlRange.strRange.startIndex
if let firstComponentInt = Int(objPlRange.strRange[..<firstIndex])
let lastComponentInt = Int(lastComponentInt[lastIndex...]) {
objPlRange.start = firstComponentInt
objPlRange.end = lastComponentInt
} else {
// the string is empty, or the values are not valid integers
}
}
I would use firstIndex and lastIndex to find non-numerical characters and then use prefix and suffix to extract the values.
Here is a function that returns the first and last int as a tuple
func firstAndLast(_ string: String) -> (Int, Int) {
guard let start = string.firstIndex(where: { !$0.isNumber }) else {
return string.isEmpty ? (0, 0) : (Int(string) ?? 0, 0)
}
guard let end = string.lastIndex(where: { !$0.isNumber }) else {
return (0, 0)
}
return (Int(string.prefix(upTo: start)) ?? 0, Int(string.suffix(from: string.index(end, offsetBy: 1))) ?? 0)
}
This is a very simple code that you can use to achieve that.
For first Number
func findFirst(number: String) -> String{
guard number != "" else {return ""}
var firstNumber = ""
for i in number{
if i.isNumber{
firstNumber.append(i)
}else{
return firstNumber
}
}
return firstNumber
}
This is for the second number
func findLast(number: String) -> String{
guard number != "" else {return ""}
var firstNumber = ""
for i in number.reversed(){
if i.isNumber{
firstNumber.append(i)
}else{
return firstNumber
}
}
return firstNumber
}
Sorry if I didn't understand your question properly, but as per my understanding here is the simplest code I can give you
let string = "2A4-206,208-212,215-220"
let numbers = string.components(separatedBy: [",","-"]).filter({ string in
if let _ = Int(string) {
return true
} else {
return false
}
})
if numbers.count == 0 {
print("Start: 0, End: 0")
} else if let first = Int(numbers.first ?? ""), numbers.count == 1 {
print("Start: \(first), End: 0")
} else if let first = Int(numbers.first ?? ""), let last = Int(numbers.last ?? "") {
print("Start: \(first), End: \(last)")
}
You can add this code in function and replace print statement with return block

What is the swift way of converting this string into PGN notation?

I have the following string
let a:String = "r0bqkb0r/pppppppp/00n00n00/00000000/000P0000/0000B000/PPP0PPPP/RN0QKBNR/"
and want to convert this to PGN notation so the final result should be
result = "r1bqkb1r/pppppppp/2n2n2/8/3P4/4B3/PPP1PPPP/RN1QKBNR/"
The PGN notation converts the zeros to counts found. Normally in python, I would just use
import chess.pgn
Before deep diving into python library, is there is a succinct and 'Swift' way to do this?
Here is a solution using reduce and a separate counter
Update, rewrote it as an extension to String
extension String {
func pgpNotation() -> String {
var zeroCounter = 0
var result = self.reduce(into: "") {
if $1 == "0" {
zeroCounter += 1
return
}
if zeroCounter > 0 {
$0.append("\(zeroCounter)")
zeroCounter = 0
}
$0.append($1)
}
if zeroCounter > 0 { result.append("\(zeroCounter)")}
return result
}
}
Examples
let x = "r00d00"
print(x.pgpNotation())
let a:String = "r0bqkb0r/pppppppp/00n00n00/00000000/000P0000/0000B000/PPP0PPPP/RN0QKBNR/"
print(a.pgpNotation())
r2d2
r1bqkb1r/pppppppp/2n2n2/8/3P4/4B3/PPP1PPPP/RN1QKBNR/
There is no direct function for that but I just created a program for fun. You can check this out:-
let str = "r0bqkb0r/pppppppp/00n00n00/00000000/000P0000/0000B000/PPP0PPPP/RN0QKBNR/"
var newStr = ""
var flag = 0
// Do any additional setup after loading the view.
for char in str {
if flag == 0 {
if char == "0" {
flag += 1
}
else {
newStr.append(char)
}
}
else {
if char == "0" {
flag += 1
}
else {
newStr.append("\(flag)")
flag = 0
if char == "0" {
flag += 1
}
else {
newStr.append(char)
}
}
}
}
print(newStr)
There doesn't exist any direct method to get the pgn notation String. You can use a forEach(_:) instead, i.e.
let a = "r0bqkb0r/pppppppp/00n00n00/00000000/000P0000/0000B000/PPP0PPPP/RN0QKBNR/"
var result = ""
var count = 0
a.forEach {
if $0 == "0" {
count += 1
} else {
if count != 0 {
result.append("\(count)")
count = 0
}
result.append($0)
}
}
print(result) //r1bqkb1r/pppppppp/2n2n2/8/3P4/4B3/PPP1PPPP/RN1QKBNR/
With a simple regex and a loop (just to propose an original solution):
let a = "r0bqkb0r/pppppppp/00n00n00/00000000/000P0000/0000B000/PPP0PPPP/RN0QKBNR/"
extension String {
var chessPGN : String {
var result = self
let regex = try! NSRegularExpression(pattern: "0+")
while let match = regex.matches(in: result, range: .init(location: 0, length: result.count)).first {
if let stringRange = Range(match.range , in: result) {
result.replaceSubrange(stringRange, with: match.range.length.description)
}
}
return result
}
}
print(a.chessPGN) // r1bqkb1r/pppppppp/2n2n2/8/3P4/4B3/PPP1PPPP/RN1QKBNR/
EDIT: A version calling only once the regex
extension String {
var chessPGN : String {
var result = self
let regex = try! NSRegularExpression(pattern: "0+")
for match in regex.matches(in: result, range: .init(location: 0, length: result.count)).sorted(by: { $0.range.location > $1.range.location }) {
if let stringRange = Range(match.range , in: result) {
result.replaceSubrange(stringRange, with: match.range.length.description)
}
}
return result
}
}

Need to find consecutive sequence like "6789" or "abcd" in Swift

I need help to find consecutive sequence for example more than 3 characters in ascending order. I've already implemented one solution but It's not universal.
Examples what should be found - "1234", "abcd", "5678".
And what shouldn't be found - "123", "adced", "123abc", "89:;"
Particularly the case "89:;", symbol ":" - is 58 in uniCode and "9" - is 57, that's why my approach does not work in the case.
Implementation should be in swift.
Additional clarification
For now it would be enough to find the sequences only in English letters and numbers.
private func findSequence(sequenceLength: Int, in string: String) -> Bool {
let scalars = string.unicodeScalars
var unicodeArray: [Int] = scalars.map({ Int($0.value) })
var currentLength: Int = 1
var i = 0
for number in unicodeArray {
if i+1 >= unicodeArray.count {
break
}
let nextNumber = unicodeArray[i+1]
if number+1 == nextNumber {
currentLength += 1
} else {
currentLength = 1
}
if currentLength >= sequenceLength {
return true
}
i += 1
}
return false
}
var data = [1,2,5,4,56,6,7,9,6,5,4,5,1,2,5,4,56,6,7,9,8,1,1,2,5,4,56,6,7,9,8,1,1,2,5,4,56,6,7,9,8,1,1,2,5,4,56,6,7,9,8,1,1,2,5,4,56,6,7,9,8,11,2,5,4,56,6,7,9,8,1,2,3]
for i in 0...data.count{
if i+2 < data.count{
if Int(data[i] + data[i+2]) / 2 == data[i+1] && Int(data[i] + data[i+2]) % data[i+1] == 0 && data[i+1] != 1 && data[i] < data[i+1]{
print(data[i] ,data[i+1], data[i+2])
}
}
}
You can check for sequence with CharacterSet
func findSequence(sequenceLength: Int, in string: String) -> Bool {
// It would be better to extract this out of func
let digits = CharacterSet.decimalDigits
let lowercase = CharacterSet(charactersIn: "a"..."z")
let uppercase = CharacterSet(charactersIn: "A"..."Z")
let controlSet = digits.union(lowercase).union(uppercase)
// ---
let scalars = string.unicodeScalars
let unicodeArray = scalars.map({ $0 })
var currentLength: Int = 1
var i = 0
for number in unicodeArray where controlSet.contains(number) {
if i+1 >= unicodeArray.count {
break
}
let nextNumber = unicodeArray[i+1]
if UnicodeScalar(number.value+1) == nextNumber {
currentLength += 1
} else {
currentLength = 1
}
if currentLength >= sequenceLength {
return true
}
i += 1
}
return false
}
I did assumed that "a" ... "z" and "A"..."Z" are consecutive here, to make it in range, but it may be better do explicitly list all the symbols you want.
Or use CharacterSet.alphanumerics, but is not limited to basic latin alphabet.

Levenshtein distance in Swift3

I'm using a tutorial from Rosetta Code to calculate Levenshtein distance. It seems their code is in Swift2 so I get this error Binary operator '+' cannot be applied to operands of type '[Int]' and 'Repeated<String.CharacterView>' when doing this: var cur = [i + 2] + empty where let empty = repeatElement(s, count: 0). How can I go about this?
There were a couple of changes to make.
The construction of the Array empty.
enumerate() is now enumerated()
successor() doesn't exist anymore so I replaced it with +1
So the function is now
Swift 4:
func levDis(_ w1: String, _ w2: String) -> Int {
let empty = [Int](repeating:0, count: w2.count)
var last = [Int](0...w2.count)
for (i, char1) in w1.enumerated() {
var cur = [i + 1] + empty
for (j, char2) in w2.enumerated() {
cur[j + 1] = char1 == char2 ? last[j] : min(last[j], last[j + 1], cur[j]) + 1
}
last = cur
}
return last.last!
}
Swift 3:
func levDis(w1: String, w2: String) -> Int {
let (t, s) = (w1.characters, w2.characters)
let empty = Array<Int>(repeating:0, count: s.count)
var last = [Int](0...s.count)
for (i, tLett) in t.enumerated() {
var cur = [i + 1] + empty
for (j, sLett) in s.enumerated() {
cur[j + 1] = tLett == sLett ? last[j] : min(last[j], last[j + 1], cur[j])+1
}
last = cur
}
return last.last!
}
Updated and improved answer to Swift 4, based on #Spads answer.
extension String {
func levenshteinDistanceScore(to string: String, ignoreCase: Bool = true, trimWhiteSpacesAndNewLines: Bool = true) -> Float {
var firstString = self
var secondString = string
if ignoreCase {
firstString = firstString.lowercased()
secondString = secondString.lowercased()
}
if trimWhiteSpacesAndNewLines {
firstString = firstString.trimmingCharacters(in: .whitespacesAndNewlines)
secondString = secondString.trimmingCharacters(in: .whitespacesAndNewlines)
}
let empty = [Int](repeating:0, count: secondString.count)
var last = [Int](0...secondString.count)
for (i, tLett) in firstString.enumerated() {
var cur = [i + 1] + empty
for (j, sLett) in secondString.enumerated() {
cur[j + 1] = tLett == sLett ? last[j] : Swift.min(last[j], last[j + 1], cur[j])+1
}
last = cur
}
// maximum string length between the two
let lowestScore = max(firstString.count, secondString.count)
if let validDistance = last.last {
return 1 - (Float(validDistance) / Float(lowestScore))
}
return 0.0
}
}
infix operator =~
func =~(string: String, otherString: String) -> Bool {
return string.levenshteinDistanceScore(to: otherString) >= 0.85
}
func ~=(string: String, otherString: String) -> Bool {
return string.levenshteinDistanceScore(to: otherString) >= 0.85
}
Since #Daniel Illescas answer is not working, here is working version with Int return type and with assert.
extension String {
func levenshteinDistance(to string: String, ignoreCase: Bool = true, trimWhiteSpacesAndNewLines: Bool = true) -> Int {
var firstString = self
var secondString = string
if ignoreCase {
firstString = firstString.lowercased()
secondString = secondString.lowercased()
}
if trimWhiteSpacesAndNewLines {
firstString = firstString.trimmingCharacters(in: .whitespacesAndNewlines)
secondString = secondString.trimmingCharacters(in: .whitespacesAndNewlines)
}
let empty = [Int](repeating: 0, count: secondString.count)
var last = [Int](0...secondString.count)
for (i, tLett) in firstString.enumerated() {
var cur = [i + 1] + empty
for (j, sLett) in secondString.enumerated() {
cur[j + 1] = tLett == sLett ? last[j] : Swift.min(last[j], last[j + 1], cur[j]) + 1
}
last = cur
}
if let validDistance = last.last {
return validDistance
}
assertionFailure()
return 0
}
}