Leetcode 38: The “count-and-say” sequence
Your iterative solution

This is generally fine, with the only exception that it operates on an array of strings –  I'll come back to that later.

Variables should be defined in the narrowest scope where they are used. This applies to


var temp: [String], j: Int



which are used only in the inner loop. Both variable names are non-descriptive and can be improved (e.g.: j actually counts the number of consecutive equal numbers).

Your recursive solution

Here


let arr = Array(str).map{Int(String($0))}



the conversion from str to an array is not needed: A Swift string is a collection of its characters, and you can call map() directly on the string. You also know that the string contains only decimal digits. Therefore you can force-unwrap here, replacing several force-unwraps in the following code:

let arr = str.map{ Int(String($0))! }


This


var final = temp.last!
final.count += 1
temp.removeLast()
temp.append(final)



is more simply and more efficiently done as

temp[temp.count - 1].count += 1


And this loop


var result = ""
for key in temp{
    result += "\(key.0)\(key.1)"
}
return result



can be simplified using map():

return temp.map { "\($0.count)\($0.val)" }.joined()


Generally, this looks less efficient to me than the first approach, because a string is converted to an integer array in each recursion step.

Redesign

In order to avoid unnecessary conversions between strings, characters, and integers, I would suggest to operate on an integer array generally, and create the string answer only as the final step.

Instead of an inner loop or recursion I would put the code to compute the next iteration into a separate function. That makes the code easier to read and to test:

class Solution {
    func countAndSay(_ n: Int) -&gt; String {
        var seq = [1]
        for _ in 1..&lt;n {
            seq = transform(seq)
        }
        return seq.map(String.init).joined()
    }

    func transform(_ seq: [Int]) -&gt; [Int] {
        // ... TODO ...
    }
}


This works with both your solutions. For your first solution the transform function would be something like this:

func transform(_ seq: [Int]) -&gt; [Int] {
    var result = [Int]() // The transformed sequence
    var current = seq[0] // The current number
    var count = 1 // Counts # of consecutive equal numbers
    for num in seq.dropFirst() {
        if num == current {
            count += 1
        } else {
            result.append(count)
            result.append(current)
            current = num
            count = 1
        }
    }
    result.append(count)
    result.append(current)
    return result
}


Instead of comparing against arr[i-1] the current number is “remembered” in a local variable, so that we can iterate over the sequence (without its first element) instead of iterating over the array indices.

Your second solution creates an array of (count, value) tuples first, and therefore does not need to handle the case of the last run separately. If you start with an empty array of tuples (and use optional chaining to compare against the last value) then you even don't have to handle the first value separately:

func transform(_ seq: [Int]) -&gt; [Int] {

    var pairs: [(count: Int, val: Int)] = []
    for num in seq {
        if num == pairs.last?.val {
            pairs[pairs.count - 1].count += 1
        } else {
            pairs.append((count: 1, val: num))
        }
    }
    return Array(pairs.map { [$0.count, $0.val] }.joined())
}


In my tests this was a bit faster than your original solution, but slower than the first approach, probably because of the intermediate array.

A third solution

Here is another possible solution, which combines the advantages of both approaches. Instead of traversing the array elements, we directly search for the next index of a number different from the current. Neither the first nor the last run has be be treated specially:

func transform(_ seq: [Int]) -&gt; [Int] {
    var result = [Int]()
    var fromIndex = seq.startIndex // Start index of current run
    while fromIndex != seq.endIndex {
        let current = seq[fromIndex]
        // Start index of next run:
        let toIndex = seq[(fromIndex+1)...].firstIndex(where: { $0 != current }) ?? seq.endIndex
        result.append(toIndex - fromIndex)
        result.append(current)
        fromIndex = toIndex
    }
    return result
}