Leetcode 38: The “count-and-say” sequence 38. Count and Say
  
  The count-and-say sequence is the sequence of integers with the first five terms as following:

1.     1
2.     11
3.     21
4.     1211
5.     111221

  
  1 is read off as "one 1" or 11.
  
  11 is read off as "two 1s" or 21.
  
  21 is read off as "one 2, then one 1" or 1211.
  
  Given an integer n where 1 ≤ n ≤ 30, generate the nth term of the count-and-say sequence.
  
  Note: Each term of the sequence of integers will be represented as a string.


With iteration:

How to reduce 3 kinds of situation to one solution?

class Solution {
    func countAndSay(_ n: Int) -&gt; String {
        var ans = ["1"]
        var temp: [String], j: Int
        for _ in 1..&lt;n{
            //  from the beginning 1 to what we what
            j = 1
            temp = []
            for i in 1..&lt;ans.count{
                if ans[i - 1] == ans[i]{
                    j += 1
                    //  Situation one: the current is equal to the previous ( util the last )
                }
                else{
                    temp.append(String(j))
                    temp.append(ans[i-1])
                    //  Situation two: the current is not equal to the previous ( util the last )
                    j = 1
                }
            }
            //  Situation three: the last value
            temp.append(String(j))
            temp.append(ans.last!)
            ans = temp
            print(ans.reduce("", +))
        }
        return ans.reduce("", +)
    }
}


With Recursion, which is a little leaner.

class Solution {
    func countAndSay(_ n: Int) -&gt; String {
        if n == 1 {
            return "1"
        }
        let str = countAndSay(n-1)
        let arr = Array(str).map{Int(String($0))}
        var last = arr[0]!
        var temp : [(count: Int, val: Int)] = [(1, last)]
        for num in arr[1...]{
            if last == num{
                var final = temp.last!
                final.count += 1
                temp.removeLast()
                temp.append(final)
            }
            else{
                temp += [(1, num!)]
            }
            last = num!
        }
        var result = ""
        for key in temp{
            result += "\(key.0)\(key.1)"
        }
        return result
    }
}


How to go on doing some improvement?
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
}