You chose to represent the “list of sequences” as a dictionary
var sequences: [Int: [Int]] = [:]
and here you test if a sequence for the given index already exists, and then either append a new element or create the initial sequence for that index:
if var sequence = sequences[seq] { sequence.append(y) sequences[seq] = sequence } else { sequences[seq] = [y] }
That can be greatly simplified by using a subscript with a default value:
sequences[seq, default: []].append(y)
But actually, instead of representing the “list of sequences” as a dictionary I would represent it as an array (of arrays) instead:
var sequences: [[Int]] = Array(repeating: [], count: n)
The number of sequences is a-priori known, so that there is no advantage of using a dictionary. Appending a new element to a sequences (query type 1) then becomes
sequences[seq].append(y)
and for query type 2 we get
lastAnswer = sequences[seq][index] answerlist.append(lastAnswer)
without the need of forced-unwrapping for the dictionary subscripting.
This should also be more efficient, because (array) index lookups are faster than dictionary lookups.
Some more remarks:
- The type of a variable should not be part of the variable name, e.g.
answers
instead of answerList
. Multiple (related) assignments can be combined to a tuple assignment, e.g.
let (queryType, x, y) = (query[0], query[1], query[2])
We know that the query type can only be 1 or 2, but a fatalError()
in the default case helps to find programming errors.
Putting it together, the function could look like this:
func dynamicArray(n: Int, queries: [[Int]]) -> [Int] { var sequences: [[Int]] = Array(repeating: [], count: n) var lastAnswer = 0 var answers = [Int]() for query in queries { let (queryType, x, y) = (query[0], query[1], query[2]) switch queryType { case 1: let seq = (x ^ lastAnswer) % n sequences[seq].append(y) case 2: let seq = (x ^ lastAnswer) % n let index = y % sequences[seq].count lastAnswer = sequences[seq][index] answers.append(lastAnswer) default: fatalError("Bad query type") } } return answers }
In addition, it seems that most of the time is spent while reading the input data into the queries
array, done by the (HackerRank supplied template) code
guard let queriesRowTemp = readLine()?.replacingOccurrences(of: "\\s+$", with: "", options: .regularExpression) else { fatalError("Bad input") } let queriesRow: [Int] = queriesRowTemp.split(separator: " ").map { if let queriesItem = Int($0) { return queriesItem } else { fatalError("Bad input") } }
Instead of removing trailing whitespace with a regular expression search we can split the input row and ignore empty components:
guard let queriesRowTemp = readLine() else { fatalError("Bad input") } let queriesRow: [Int] = queriesRowTemp.split(separator: " ", omittingEmptySubsequences: true).map { if let queriesItem = Int($0) { return queriesItem } else { fatalError("Bad input") } }
In my test that cut the time to read 100000 queries down from 3.7 to 1.7 seconds.