Mistrz Programowania

Mistrz Programowania 2023 runda 3 poziom d
Tagi: programowanie dynamiczne kombinatoryka
Autor omówienia: Tomasz Kwiatkowski
Treść

Omówienie zadania Nieudany szyfr

Zadanie rozwiążemy dynamicznie. Niech dp[i] oznacza ile słów daje szyfr złożony z pierwszych $i$ cyfr wejścia. Dla zeru znaków mamy $1$ sposób – puste słowo, dp[0] = 1.

Dalej zastanówmy się, jak obliczyć dp[i]. Mamy dwie opcje albo ostatni znak ma kod jednocyfrowy, albo dwucyfrowy. Zatem jeżeli ostatnia cyfra nie jest zerem, to do dp[i] dodajemy dp[i - 1]. Jeżeli ostatnie dwie cyfry tworzą liczbę z zakresu $[10, 26]$, to do dp[i] dodajemy dp[i - 2] (oczywiście, jeżeli $i > 1$). Wynik będzie w ostatniej komórce dp.

Zauważmy, że tak naprawdę nie potrzebujemy pamiętać wszystkich wartości tablicy dp. Wystarczą nam dwie ostatnie. W rozwiązaniu używam dwóch zmiennych – a oznaczającej dp[i - 2] oraz b oznaczającej dp[i - 1]. Funkcja ok_char zwraca wartość logiczną i mówi, czy ostatnie $d+1$ cyfr odpowiada jakiemuś znakowi lub nie.

Przykładowe implementacje

C++

#include <bits/stdc++.h>
using namespace std;

const int MOD = 1e9 + 7;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    string s;
    cin >> s;
    int a = 1, b = 1;
    auto ok_char = [&](int i, int d) {
        return (d == 0 && s[i] > '0') ||
               (d == 1 && s[i - 1] == '1') ||
               (d == 1 && s[i - 1] == '2' && s[i] < '7');
    };
    for (int i = 1; i < int(s.size()); ++i) {
        int c = (ok_char(i, 1) * a + ok_char(i, 0) * b) % MOD;
        a = b;
        b = c;
    }
    cout << b << '\n';
    return 0;
}

Python

MOD = 1000000007

def main():
    s = input()
    a = b = 1
    for c1, c2 in zip(s[:-1], s[1:]):
        a, b = b, (a * (c1 == '1' or (c1 == '2' and c2 < '7')) + b * (c2 > '0')) % MOD
    print(b)

main()

Uwagi