LeetCode - Count and Say

Problem statement

The count-and-say sequence is a sequence of digit strings defined by the recursive formula:

  • countAndSay(1) = '1'

  • countAndSay(n) is the way you would 'say' the digit string from countAndSay(n-1), which is then converted into a different digit string.

To determine how you 'say' a digit string, split it into the minimal number of substrings such that each substring contains exactly one unique digit. Then for each substring, say the number of digits, then say the digit. Finally, concatenate every said digit.

For example, the saying and conversion for digit string '3322251':

Container

Given a positive integer n, return the nth term of the count-and-say sequence.

Problem statement taken from: https://leetcode.com/problems/count-and-say

Example 1:

Input: n = 1
Output: '1'
Explanation: This is the base case.

Example 2:

Input: n = 4
Output: '1211'
Explanation:
countAndSay(1) = '1'
countAndSay(2) = say '1' = one 1 = '11'
countAndSay(3) = say '11' = two 1's = '21'
countAndSay(4) = say '21' = one 2 + one 1 = '12' + '11' = '1211'

Constraints:

- 1 <= n <= 30

Explanation

Brute force approach

A naive approach is to generate all the terms from 1 to n. We generate the next term using the current term by scanning it left to right. While scanning it, we keep track of the count of all consecutive characters. For the matching characters, we append the count of the character and append the character to the count.

A C++ snippet of this approach is as follows:

string countAndSay(int n) {
    if (n == 1) {
        return '1';
    }
    if (n == 2) {
        return '11';
    }

    string current = '11';
    string tmp = '';
    int counter = 1;
    int len;

    for (int i = 3; i <= n; i++) {
        current += '$';
        len = str.length();

        counter = 1;
        tmp = '';

        for (int j = 1; j < len; j++) {
            if (current[j] != current[j - 1]) {
                tmp += counter + '0';
                tmp += current[j-1];
                counter = 1;
            } else {
                counter++;
            }
        }

        current = tmp;
    }

    return current;
}

The time complexity of this approach is O(n^2). The space complexity is O(1).

Dynamic Programming

We can use Dynamic Programming to generate each row of the count and say pattern based on the previous row. We store all the results in a vector of strings to avoid repeating the same calculations for multiple rows. This will reduce the overall time complexity of the solution.

Let's check the algorithm to understand it better.

Algorithm

- initialize a vector of string
  vector<string> dp(n + 1, '')

  set dp[1] = '1'

  initialize current and next string
  string current, next;

  set counter to 1
  int counter = 1

- loop for i = 2; i <= n; i++
  - set current = dp[i - 1]
  - set next = ''
  - set counter = 1

  - loop for j = 1; j < current.size(); j++
    - if current[j - 1] == current[j]
      - update counter = counter + 1
    - else
      - update next = next + to_string(counter) + current[j - 1]
      - set counter = 1
    - if end

    - update next = next + to_string(counter) + current.back()

    - dp[i] = next
  - for end

- for end

- return dp[n]

C++ solution

class Solution {
public:
    string countAndSay(int n) {
        vector<string> dp(n + 1, '');
        dp[1] = '1';
        string current, next;
        int counter;

        for(int i = 2; i <= n; i++) {
            current = dp[i - 1];
            next = '';

            counter = 1;

            for(int j = 1; j < current.size(); j++) {
                if(current[j - 1] == current[j]) {
                    counter++;
                } else {
                    next += to_string(counter) + current[j - 1];
                    counter = 1;
                }
            }

            next += to_string(counter) + current.back();

            dp[i] = next;
        }

        return dp[n];
    }
};

Golang solution

func countAndSay(n int) string {
    dp := make([]string, n + 1)
    dp[1] = '1'
    var current, next string
    counter := 1

    for i := 2; i <= n; i++ {
        current = dp[i - 1]
        next = ''

        counter = 1

        for j := 1; j < len(current); j++ {
            if current[j - 1] == current[j] {
                counter++
            } else {
                next += strconv.Itoa(counter) + string(current[j - 1])
                counter = 1
            }
        }

        next += strconv.Itoa(counter) + current[len(current) - 1:]

        dp[i] = next
    }

    return dp[n]
}

JavaScript solution

var countAndSay = function(n) {
    let dp = new Array(n + 1).fill('');
    dp[1] = '1';
    let current, next;
    let counter;

    for(let i = 2; i <= n; i++) {
        current = dp[i - 1];
        next = '';

        counter = 1;

        for(let j = 1; j < current.length; j++) {
            if(current[j - 1] == current[j]) {
                counter++;
            } else {
                next += counter.toString() + current[j - 1];
                counter = 1;
            }
        }

        next += counter.toString() + current.slice(-1);

        dp[i] = next;
    }

    return dp[n];
};

Let's dry-run our algorithm for a few examples to see how the solution works.

Input: n = 4

Step 1: vector<string> dp(n + 1, '')
        dp(4 + 1, '')
        dp(5, '')
        dp = ['', '', '', '', '']
        dp[1] = '1'
        dp = ['', '1', '', '', '']

        string current, next
        int counter

Step 2: loop for i = 2; i <= n
          2 <= 4
          true

          current = dp[i - 1]
                  = dp[2 - 1]
                  = dp[1]
                  = '1'
          next = ''

          counter = 1

          loop for j = 1; j < current.size()
            1 < 1
            false

          next = next + to_string(counter) + current.back()
               = '' + to_string(1) + '1'
               = '' + '1' + '1'
               = '11'

          dp[i] = next
          dp[2] = '11'

          dp = ['', '1', '11', '', '']

          i++
          i = 3

Step 3: loop for i <= n
          3 <= 4
          true

          current = dp[i - 1]
                  = dp[3 - 1]
                  = dp[2]
                  = '11'
          next = ''

          counter = 1

          loop for j = 1; j < current.size()
            1 < 2
            true

            if current[j - 1] == current[j]
               current[1 - 1] == current[1]
               current[0] == current[1]
               '1' == '1'
               true

               counter++
               counter = 2

            i++
            i = 2

          loop for j < current.size()
            2 < 2
            false

          next = next + to_string(counter) + current.back()
               = '' + to_string(2) + '1'
               = '' + '2' + '1'
               = '21'

          dp[i] = next
          dp[3] = '21'

          dp = ['', '1', '11', '21', '']

          i++
          i = 4

Step 4: loop for i <= n
          4 <= 4
          true

          current = dp[i - 1]
                  = dp[4 - 1]
                  = dp[3]
                  = '21'
          next = ''

          counter = 1

          loop for j = 1; j < current.size()
            1 < 2
            true

            if current[j - 1] == current[j]
               current[1 - 1] == current[1]
               current[0] == current[1]
               '2' == '1'
               false
            else
               next = next + to_string(counter) + current[j - 1]
                    = '' + to_string(1) + current[1 - 1]
                    = '' + '1' + '2'
                    = '12'

               counter = 1

            j++
            j = 2

          loop for j < current.size()
            2 < 2
            false

          next = next + to_string(counter) + current.back()
               = '12' + to_string(1) + '1'
               = '' + '1' + '1'
               = '1211'

          dp[i] = next
          dp[4] = '1211'

          dp = ['', '1', '11', '21', '1211']

          i++
          i = 5

Step 5: loop for i <= n
          5 <= 4
          false

Step 6: return dp[n]
               dp[4]

We return the answer as '1211'.

Did you find this article valuable?

Support Alkesh Ghorpade's Blog by becoming a sponsor. Any amount is appreciated!