swift string advancedBy is slow? - swift

So I am writing in swift to practice some online judge.
Here's the issue: Longest Palindromic Substring
Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.
So I am using dp to solve it in swift:
class Solution {
func longestPalindrome(s: String) -> String {
var hash = Array(count: s.characters.count, repeatedValue: Array(count: s.characters.count, repeatedValue: false))
for i in 0 ..< s.characters.count {
hash[i][i] = true
}
var maxStart = 0
var maxEnd = 0
var maxCount = 1
for i in 1.stride(through: s.characters.count - 1, by: 1) {
for j in 0 ..< s.characters.count - 1 {
if j + i < s.characters.count {
if isValidPalindrome(j, j + i, s, hash) {
hash[j][j + i] = true
if maxCount < i + 1 {
maxCount = i
maxStart = j
maxEnd = j + i
}
}
}
else {
break
}
}
}
// construct max palindrome string, swift string is so dummy
var str = ""
for i in maxStart...maxEnd {
let index = s.characters.startIndex.advancedBy(i)
str += String(s.characters[index])
}
return str
}
func isValidPalindrome(start: Int, _ end: Int, _ s: String, _ hash: [[Bool]]) -> Bool {
// end <= s's length - 1
let startIndex = s.startIndex.advancedBy(start)
let endIdnex = s.startIndex.advancedBy(end)
if end - start == 1 {
return s[startIndex] == s[endIdnex]
}
else {
let left = start + 1
let right = end - 1
return s[startIndex] == s[endIdnex] && hash[left][right]
}
}
}
I am thinking it's a correct one, but when I submit, always time exceeded for long strings like:
"kyyrjtdplseovzwjkykrjwhxquwxsfsorjiumvxjhjmgeueafubtonhlerrgsgohfosqssmizcuqryqomsipovhhodpfyudtusjhonlqabhxfahfcjqxyckycstcqwxvicwkjeuboerkmjshfgiglceycmycadpnvoeaurqatesivajoqdilynbcihnidbizwkuaoegmytopzdmvvoewvhebqzskseeubnretjgnmyjwwgcooytfojeuzcuyhsznbcaiqpwcyusyyywqmmvqzvvceylnuwcbxybhqpvjumzomnabrjgcfaabqmiotlfojnyuolostmtacbwmwlqdfkbfikusuqtupdwdrjwqmuudbcvtpieiwteqbeyfyqejglmxofdjksqmzeugwvuniaxdrunyunnqpbnfbgqemvamaxuhjbyzqmhalrprhnindrkbopwbwsjeqrmyqipnqvjqzpjalqyfvaavyhytetllzupxjwozdfpmjhjlrnitnjgapzrakcqahaqetwllaaiadalmxgvpawqpgecojxfvcgxsbrldktufdrogkogbltcezflyctklpqrjymqzyzmtlssnavzcquytcskcnjzzrytsvawkavzboncxlhqfiofuohehaygxidxsofhmhzygklliovnwqbwwiiyarxtoihvjkdrzqsnmhdtdlpckuayhtfyirnhkrhbrwkdymjrjklonyggqnxhfvtkqxoicakzsxmgczpwhpkzcntkcwhkdkxvfnjbvjjoumczjyvdgkfukfuldolqnauvoyhoheoqvpwoisniv"
I can get the correct result qahaq after some time, and I am wondering why it's so slow. If I write it in other language, not so bad.
I suspect the API s.startIndex.advancedBy(start) is causing it, but I checked the doc, no time complexity and no other ways to turn an int to the startIndex type?
Any ideas to replace advancedBy? Thank in advance.

For those having the same issue: I turned swift String into Array, and it gets much faster.
I also looked into Swift source code about the advancedBy implementation, it's a O(n) opreation, that's why it's slow.
For whom is interested in the implementation, take a look at https://github.com/apple/swift/blob/8e12008d2b34a605f8766310f53d5668f3d50955/stdlib/public/core/Index.swift
You will see advancedBy is merely multiple successor():
#warn_unused_result
public func advanced(by n: Distance) -> Self {
return self._advanceForward(n)
}
/// Do not use this method directly; call advanced(by: n) instead.
#_transparent
#warn_unused_result
internal func _advanceForward(_ n: Distance) -> Self {
_precondition(n >= 0,
"Only BidirectionalIndex can be advanced by a negative amount")
var p = self
var i : Distance = 0
while i != n {
p._successorInPlace()
i += 1
}
return p
}

This should do the trick. Before implementing it, I recommend checking out some explanations such as this guy's. https://www.youtube.com/watch?v=obBdxeCx_Qs. I'm not affiliated with him, though I do believe his video is somewhat useful.
func longestPalindrome(_ s: String) -> String {
var charArray = [Character("$"), Character("#")]
for i in s.characters {
charArray += [i, Character("#")]
}
charArray += [Character("#")]
var mir = 0, c = 0, r = 0, longestPalindromeIndex = 0, longestPalindromeLength = 0, ss = "", returnString = ""
var p = [Int]()
//MARK: For loop
for i in 0...(charArray.count - 1) {
p += [0, 0]
mir = 2 * c - i
if i < r {
p[i] = min(r - i, p[mir])
}
if i - (1 + p[i]) >= 0 && i + (1 + p[i]) < charArray.count - 1 {
while charArray[i + (1 + p[i])] == charArray[i - (1 + p[i])] {
p[i] += 1
}
}
if p[i] > longestPalindromeLength {
longestPalindromeIndex = i
longestPalindromeLength = p[i]
}
if i + p[i] > r {
c = i
r = i + p[i]
}
}//for loop
for i in Array(charArray[(longestPalindromeIndex - longestPalindromeLength)...(longestPalindromeIndex + longestPalindromeLength)]) {
ss = String(i)
if ss != "#" && ss != "$" && ss != "#" {
returnString += ss
}
}
return returnString
}//func

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.

Karatsuba multiplication in swift

I was trying to implement Karatsuba multiplication in swift. I wrote the below code and it is working fine for some smaller numbers but as the number gets bigger this code fails to give the correct answer. I have debugged in every possible way I can but could not find the bug. Algorithm wise I think I did correctly write the code. And the code is working fine for smaller numbers. But the final answer is wrong for bigger numbers. If anyone out there can crack down the mistake I'm making, pls do help me
func findMultiplication(x: String, y: String) -> String {
if isZero(str: x) || isZero(str: y) {
return "0"
}
var x = removeLeadingZeros(number: x)
var y = removeLeadingZeros(number: y)
if x.count < 2 || y.count < 2 {
let result = Int(x)!*Int(y)!
return String(result)
}
var middleIndexX: String.Index
var middleIndexY: String.Index
var middleIndex: Int
if x.count >= y.count {
y = addLeftPaddingZeros(numberOfZeros: x.count-y.count, for: y)
middleIndex = x.count / 2
if x.count % 2 != 0 {
middleIndex += 1
}
} else {
x = addLeftPaddingZeros(numberOfZeros: y.count-x.count, for: x)
middleIndex = y.count / 2
if y.count % 2 != 0 {
middleIndex += 1
}
}
middleIndexX = x.index(x.startIndex, offsetBy: middleIndex)
middleIndexY = y.index(y.startIndex, offsetBy: middleIndex)
let a = String(x[x.startIndex..<middleIndexX])
let b = String(x[middleIndexX..<x.endIndex])
let c = String(y[y.startIndex..<middleIndexY])
let d = String(y[middleIndexY..<y.endIndex])
let ac = findMultiplication(x: a, y: c)
let bd = findMultiplication(x: b, y: d)
let aPb = Int(a)! + Int(b)!
let cPd = Int(c)! + Int(d)!
let gauss = findMultiplication(x: String(aPb), y: String(cPd))
let thirdItem = String(Int(gauss)! - Int(ac)! - Int(bd)!)
var returnSum = 0
returnSum += Int(addLeftPaddingZeros(numberOfZeros: x.count, for: ac, isLeft: false)) ?? 0
returnSum += Int(addLeftPaddingZeros(numberOfZeros: middleIndex, for: thirdItem, isLeft: false)) ?? 0
returnSum += Int(bd) ?? 0
return String(returnSum)
}
print(findMultiplication(x: "123400", y: "123711"))
func removeLeadingZeros(number: String) -> String {
var number = number
while number.first == "0" {
number.removeFirst()
}
if number == "" {
return "0"
}
return number
}
//The function name is given like this only. BUt his will help to add padding zero in left and right also
func addLeftPaddingZeros(numberOfZeros: Int, for str: String, isLeft: Bool = true) -> String {
var padding = ""
for _ in 0 ..< numberOfZeros {
padding += "0"
}
if isLeft {
return padding+str
} else {
return str + padding
}
}
func isZero(str: String) -> Bool {
for char in str {
if char != "0" {
return false
}
}
return true
}

Compare values calculated from two strings

Two people participate in competition. There will be one easy, difficult and medium question.
Scores per difficulty:
E: 1
M: 3
H: 5
User will enter two strings (A and B) and function should find out greater score or tie.
func winner(A: String, B: String) -> String {
var sumA = 0
var sumB = 0
var sumhigh = 0
var checker: Bool = false
for i in 0..<(A.count-1) {
if (erica[i] == "E") {
sumA += 1
checker = true
} else if (A[i] == "M"){
sumA = sumA + 3
checker = true
} else if (A[i] == "H"){
sumA += 3
checker = true
}
}
return String(sumA)
}
print(winner(A: "EHH", B: "EME"))
Desired result : A Wins // How to achieve this
This will compare the two strings to determine a winner.
func checkWinner(aString: String, bString: String) -> String {
// Make lowercase for comparison, so that input case does not matter
let a = aString.lowercased()
let b = bString.lowercased()
// Dictionary of values
let scoreDict: [Character : Int] = ["e": 1, "m": 3, "h": 5]
// Score variables to increment
var aTotal = 0
var bTotal = 0
// Calculate scores
for char in a {
aTotal += scoreDict[char] ?? 0
}
for char in b {
bTotal += scoreDict[char] ?? 0
}
// Calculate winner
if aTotal == bTotal {
return "It’s a draw"
} else {
return aTotal > bTotal ? "A wins! Score: \(aTotal)" : "B wins! Score: \(bTotal)"
}
}
// Here is an example in which A wins
print(checkWinner(aString: "ehh", bString: "meh"))
check this
func phraseValue(sentence: String) -> Int
{
var count = 0
for letter in sentence
{
if letter == "E"
{
count += 1
}
else if letter == "M"
{
count += 3
}
else if letter == "H"
{
count += 5
}
}
return count
}
and finally
func winner(A: String, B: String) -> String {
var sumA = 0
var sumB = 0
sumA = valuePhrase(sentence: A)
sumB = valuePhrase(sentence: B)
if sumA == sumB
{
return "tie"
}
else if sumA > sumB
{
return String(sumA) + "A wins"
}
else
{
return String(sumB) + "B wins"
}
}

Explanation of this prefix Sum Coding Challenge from Codility GenomicRangeQuery in Swift 4.2

Could someone explain how this prefix sum calculation work as I am confused I mean I understand that it creates an array of Ints with the prefix-sums of the letters but I do not understand how? Could someone perhaps post a more naive logic or some explanation? Or perhaps a shorter version of that MAP function as it is all confusingly complicated.
Link to the challenge just in case someone would like to have a look at it
public func solution(_ S : inout String, _ P : inout [Int], _ Q : inout [Int]) -> [Int] {
//The mapping below needs the explanation
var prefixSumA = S.map({ () -> (Character) -> Int in
var s = 0; return {
if $0 == "A" {
return (s += 1, s).1
}
return s
}
}())//until here
prefixSumA.insert(0, at: 0)
var prefixSumC = S.map({ () -> (Character) -> Int in
var s = 0; return {
if $0 == "C" {
return (s += 1, s).1
}
return s
}
}())
prefixSumC.insert(0, at: 0)
var prefixSumG = S.map({ () -> (Character) -> Int in
var s = 0; return {
if $0 == "G" {
return (s += 1, s).1
}
return s
}
}())
prefixSumG.insert(0, at: 0)
let iterations = min(P.count, Q.count)
var result = [Int]()
for i in 0...iterations - 1 {
let p = P[i]
let q = Q[i] + 1
if prefixSumA[q] - prefixSumA[p] > 0 {
result.append(1)
} else if prefixSumC[q] - prefixSumC[p] > 0 {
result.append(2)
} else if prefixSumG[q] - prefixSumG[p] > 0 {
result.append(3)
} else {
result.append(4)
}
}
return result
}
prefixSumA calculates the number of As from the start to every given index. The same can be said about prefixSumC, and prefixSumG.
For example, if the string S is "CAGCCTA", we'll have:
prefixSumA = [0, 0, 1, 1, 1, 1, 1, 2]
prefixSumC = [0, 1, 1, 1, 2, 3, 3, 3]
prefixSumG = [0, 0, 0, 1, 1, 1, 1, 1]
(Notice the zero inserted at the beginning.)
This block of code:
var prefixSumA = S.map({ () -> (Character) -> Int in
var s = 0; return {
if $0 == "A" {
return (s += 1, s).1
}
return s
}
}())
prefixSumA.insert(0, at: 0)
returns a closure that, if the character is A, would capture the current value of s (which is the last value in the array), increment it, end return it (s).
return (s += 1, s).1 is a fancy way, that could be written as:
s += 1
return s
The whole block can be written more simply:
var prefixSumA = [0]
var countOfA = 0
prefixSumA += S.map { char in
if char == "A" { countOfA += 1 }
return countOfA
}
The same can be done for prefixSumC and prefixSumG.
I tried this one and got 62%. Performance Tests Fails.
public func solution(_ S : inout String, _ P : inout [Int], _ Q : inout [Int]) -> [Int] {
// write your code in Swift 4.2.1 (Linux)
var retArr = [Int]()
var chrArr = [Character]()
for chr in S {
chrArr.append(chr)
}
for i in 0..<P.count {
var minFactor = 4
if P[i] - Q[i] == 0 {
if chrArr[P[i]] == "A"{
minFactor = 1
}else if chrArr[P[i]] == "C"{
minFactor = 2
}else if chrArr[P[i]] == "G"{
minFactor = 3
}
}else {
for j in P[i]...Q[i] {
if chrArr[j] == "A"{
minFactor = 1
break
}else if chrArr[j] == "C"{
minFactor = 2
}else if chrArr[j] == "G"{
if minFactor > 2 {
minFactor = 3
}
}
}
}
retArr.append(minFactor)
}
return retArr
}

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.