Постановка задачи

Сообщение, содержащее буквы от A до Z, можно закодировать в числа, используя следующее сопоставление:

'A' -> "1"
'B' -> "2"
...
'Z' -> "26"

Чтобы декодировать закодированное сообщение, все цифры должны быть сгруппированы, а затем преобразованы обратно в буквы с использованием обратного сопоставления выше (может быть несколько способов). Например, "11106" можно преобразовать в:

"AAJF" with the grouping (1 1 10 6)
"KJF" with the grouping (11 10 6)

Обратите внимание, что группировка (1 11 06) недействительна, потому что «06» не может быть преобразовано в «F», так как «6» отличается от «06».

Получив строку s, содержащую только цифры, возвратите количество способов ее декодирования.

Ответ гарантированно помещается в 32-битное целое число.

Постановка задачи взята с: https://leetcode.com/problems/decode-ways

Пример 1:

Input: s = "12"
Output: 2
Explanation: "12" could be decoded as "AB" (1 2) or "L" (12).

Пример 2:

Input: s = "226"
Output: 3
Explanation: "226" could be decoded as "BZ" (2 26), "VF" (22 6), or "BBF" (2 2 6).

Пример 3:

Input: s = "0"
Output: 0
Explanation: There is no character that is mapped to a number starting with 0.
The only valid mappings with 0 are 'J' -> "10" and 'T' -> "20", neither of which start with 0.
Hence, there are no valid ways to decode this since all digits need to be mapped.

Пример 4:

Input: s = "06"
Output: 0
Explanation: "06" cannot be mapped to "F" because of the leading zero ("6" is different from "06").

Ограничения:

- 1 <= s.length <= 100
- s contains only digits and may contain leading zero(s).

Объяснение

Решение грубой силы

Наивный подход состоит в том, чтобы сгенерировать все возможные комбинации и подсчитать количество правильных последовательностей.

Этот подход легко реализовать, но его временная сложность составляет O(2^N).

Динамическое программирование

Проблема может быть решена с использованием подхода динамического программирования.

Возьмем строку "12". Мы можем декодировать строку двумя способами: [1, 2] или 12. Теперь добавим 6 в конец строки. Для новой строки способы декодирования 2 + 1 = 3. 2 для [1, 2, 3] или [12, 3] и 1 для [1, 23].

Сначала мы решили подзадачу и использовали ее решение для решения большей проблемы. Это не что иное, как подход динамического программирования.

Проверим алгоритм.

- initialize count array: count[n + 1]
- set count[0] = count[1] = 1
- if s[0] == 0 // first character of string is 0
  - return 0
- loop for i = 2; i <= n; i++
  - set count[i] = 0
  // if string is "02" we should not count "02" as a valid case.
  // But if the previous char is greater than 0 we set the current index count same
  // as the previous index count.
  - if s[i - 1] > '0'
    - count[i] = count[i - 1]
  // if string is "32" it is not possible to map to any character.
  // hence we have (i - 2)th index for 1 or 2 and
  // if s[i - 2] is 2 we additionally check for (i - 1)th index to
  // be less than 7.
  - if s[i - 2] == '1' || (s[i - 2] == '2' && s[i - 1] < '7')
    - count[i] += count[i - 2]
- return count[n]

С++ решение

class Solution {
public:
    int countWays(string s, int n){
        int count[n + 1];
        count[0] = 1;
        count[1] = 1;
        if(s[0] == '0')
            return 0;
        for(int i = 2; i <= n; i++){
            count[i] = 0;
            if(s[i - 1] > '0')
                count[i] = count[i - 1];
            if(s[i - 2] == '1' || (s[i - 2] == '2' && s[i - 1] < '7')){
                count[i] += count[i - 2];
            }
        }
        return count[n];
    }
public:
    int numDecodings(string s) {
        return countWays(s, s.size());
    }
};

Голанг решение

func numDecodings(s string) int {
    count := make([]int, len(s) + 1)
    count[0], count[1] = 1, 1
    if s[0] == '0' {
        return 0
    }
    for i := 2; i <= len(s); i++ {
        if s[i - 1] > '0' {
            count[i] = count[i - 1]
        }
        if s[i - 2] == '1' || (s[i - 2] == '2' && s[i - 1] < '7') {
            count[i] += count[i - 2]
        }
    }
    return count[len(s)]
}

Javascript-решение

var numDecodings = function(s) {
    let count = [];
    count[0] = 1;
    count[1] = 1;
    for(let i = 2; i <= s.length; i++){
        count[i] = 0;
        if(s[i - 1] > '0'){
            count[i] = count[i - 1];
        }
        if(s[i - 2] == '1' || (s[i - 2]) == '2' && s[i - 1] < '7'){
            count[i] += count[i - 2];
        }
    }
    return count[s.length];
};

Давайте запустим наш алгоритм в пробном режиме, чтобы увидеть, как работает решение.

Input: s = "226"

Step 1: int count[n + 1]
        count[0] = count[1] = 1

Step 2: if s[0] == '0'
        '2' == '0'
        false

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

        if s[i - 1] > '0'
        s[2 - 1] > '0'
        s[1] > '0'
        '2' > '0'
        true

        count[i] = count[i - 1]
        count[2] = count[2 - 1]
                 = count[1]
                 = 1

        if s[i - 2] == '1' || (s[i - 2] == '2' && s[i - 1] < '7'))
        s[2 - 2] == '1'
        s[0] == '1'
        '2' == '1'
        false

        s[i - 2] == '2' && s[i - 1] < '7'
        s[2 - 2] == '2' && s[2 - 1] < '7'
        s[0] == '2' && s[1] < '7'
        '2' == '2' && '2' < '7'
        true

        count[2] = count[i] + count[i - 2]
                 = count[2] + count[2 - 2]
                 = 1 + 1
                 = 2

        i++
        i = 3

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

        if s[i - 1] > '0'
        s[3 - 1] > '0'
        s[2] > '0'
        '6' > '0'
        true

        count[i] = count[i - 1]
        count[3] = count[3 - 1]
                 = count[2]
                 = 2

        if s[i - 2] == '1' || (s[i - 2] == '2' && s[i - 1] < '7'))
        s[3 - 2] == '1'
        s[1] == '1'
        '2' == '1'
        false

        s[i - 2] == '2' && s[i - 1] < '7'
        s[3 - 2] == '2' && s[3 - 1] < '7'
        s[1] == '2' && s[2] < '7'
        '2' == '2' && '6' < '7'
        true

        count[3] = count[i] + count[i - 2]
                 = count[3] + count[3 - 2]
                 = 2 + 1
                 = 3

        i++
        i = 4

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

Step 6: return count[n]
        count[3] = 3

So the answer we return is 3.

Первоначально опубликовано на https://alkeshghorpade.me.