неділю, 13 червня 2021 р.

Розв'язок задачі SubmultiplesOfN з Топкодер.

У цьому пості розгляну як розв'язати задачу SubmultiplesOfN. Це задача середньої складності з першого дивізіону, що була частиною SRM 805. У ній потрібно було знайти скільки разів зустрічаються всі можливі кратні числу $N$ в числі $B$. Під зустрічаються мається на увазі чи є число представлене в текстовому вигляді в десятковій системі підпослідовністю такого ж представлення іншого числа. Наприклад, "23" зустрічається в "213", але не в "312".

Для розв'язання цієї задачі ми використаємо динамічне програмування.

Для того, щоб зрозуміти, що покласти в кожну комірку таблиці нам треба згадати, що в десятковій системі $abcd = ((a \times 10 + b) \times 10 + c) \times 10 + d. $ І ще, що для будь-якого додатного $N $ $abcd \% N = ((a \times 1000) \% N + (b \times 100) \% N + (c \times 10) \% N + d \% N) \% N. $

Зауважимо, що якщо $a \% N$ рівне нулю, то $a$ кратне $N$, тобто, якщо $a$ присутнє в $B,$ то ми вже маємо врахувати це додавши одиничку до відповіді. Власне, саме таку перевірку для всіх цифр окрім нуля ми і робимо коли ініціалізуємо перший рядок таблиці.

Виходить, що кожна комірка $(i, j)$ представляє всі можливі числа, що закінчуються на $B[i],$ зустрічаються в $B[0..i]$ і мають залишок $j$ при діленні на $N.$

const auto Mod = 1000000007ll;

const auto MaxDigitsCount = 5000;
const auto MaxN = 1000;
const auto Base = 10;

long long DP[MaxDigitsCount + 1][MaxN + 1];
long long Next[MaxDigitsCount + 1][Base];

class Solution {

    static const auto kNoPosistion = -1;

    // Швидкий пошук необхідної цифри починаючи із заданої позиції
    void InitTransitions(string b)
    {
        const auto len = (int)b.length();
        
        for (auto d = 0; d < Base; ++d)
        {
            Next[len][d] = kNoPosistion;
        }

        for (auto i = len - 1; i >= 0; --i)
        {
            for (auto d = 0; d < Base; ++d)
            {
                Next[i][d] = Next[i + 1][d];
            }
            Next[i][b[i] - '0'] = i;
        }
    }

public:
    int count(string B, int N)
    {
        InitTransitions(B);

        auto result = (long long)0;

        const auto len = (int)B.length();
        
        for (auto i = 0; i < len + 1; ++i)
        {
            for (auto j = 0; j < N + 1; ++j)
            {
                DP[i][j] = 0;
            }
        }

        // Ініціалізуємо перший рядок таблиці
        for (auto d = 1; d < Base; ++d)
        {
            const auto nextDigitPosition = Next[0][d];

            if (nextDigitPosition != kNoPosistion)
            {
                const auto reminder = d % N;
                DP[nextDigitPosition][reminder] += 1;

                if (reminder == 0)
                {
                    result += 1;
                }
            }
        }

        for (auto position = 0; position < len - 1; ++position)
        {
            for (auto reminder = 0; reminder < N; ++reminder)
            {
                for (auto d = 0; d < Base; ++d)
                {
                    const auto nextDigitPosition = Next[position + 1][d];
                    
                    if (nextDigitPosition != kNoPosistion)
                    {
                        const auto appended = reminder * 10 + d;
                        const auto newReminder = appended % N;
                        const auto upToD = DP[position][reminder];

                        (DP[nextDigitPosition][newReminder] += upToD) %= Mod;

                        if (newReminder == 0)
                        {
                            (result += upToD) %= Mod;
                        }
                    }
                }
            }
        }

        return result;
    }
};

#pragma comment(linker, "/STACK:200000000")

int main()
{
    // В дужках правильна відповідь.
    cout << Solution().count("12", 2) << " (2)" << endl;
    cout << Solution().count("14", 7) << " (1)" << endl;
    cout << Solution().count("10", 2) << " (1)" << endl;
    cout << Solution().count("12345678", 2) << " (170)" << endl;
    cout << Solution().count("1111111111", 7) << " (1)" << endl;
    cout << Solution().count("1357913579135791357913579", 2) << " (0)" << endl;
    cout << Solution().count("1122334455", 6) << " (20)" << endl;
    cout << Solution().count("1020402", 24) << " (6)" << endl;
    cout << Solution().count("123456789012345678901234567890", 1) << " (62224120)" << endl;
    
}

Немає коментарів:

Дописати коментар