главная Π½ΠΎΠ²ΠΎΠ΅ Π»ΡƒΡ‡ΡˆΠ΅Π΅ Π½Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ

Π—Π°Π΄Π°Ρ‡Π° - 1639. Number of Ways to Form a Target String Given a Dictionary

πŸ” ОписаниС Π·Π°Π΄Π°Ρ‡ΠΈ

Π”Π°Π½ΠΎ: список строк words ΠΎΠ΄ΠΈΠ½Π°ΠΊΠΎΠ²ΠΎΠΉ Π΄Π»ΠΈΠ½Ρ‹ ΠΈ строка target.
ЦСль: ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΡ‚ΡŒ, сколькими способами ΠΌΠΎΠΆΠ½ΠΎ ΡΠΎΠ±Ρ€Π°Ρ‚ΡŒ строку target, слСдуя ΠΏΡ€Π°Π²ΠΈΠ»Π°ΠΌ:

πŸ’‘ ИдСя

ΠœΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ динамичСского программирования. Для быстрого обновлСния состояний Π”ΠŸ ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ подсчитаСм частоты символов для ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΈ Π² words.

Π€ΠΎΡ€ΠΌΡƒΠ»Π° Π”ΠŸ:

ΠŸΡƒΡΡ‚ΡŒ dp[i][j] β€” количСство способов ΡΠΎΠ±Ρ€Π°Ρ‚ΡŒ ΠΏΠ΅Ρ€Π²Ρ‹Π΅ i символов строки target, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ ΠΏΠ΅Ρ€Π²Ρ‹Π΅ j-ΠΉ ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΈ Π² строках words. Π’ΠΎΠ³Π΄Π°:

dp[i][j] = dp[i][jβˆ’1] + dp[iβˆ’1][jβˆ’1]β‹…freqj(target[i]]), Π³Π΄Π΅:

ΠœΡ‹ ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·ΠΈΡ€ΡƒΠ΅ΠΌ ΠΏΠ°ΠΌΡΡ‚ΡŒ, сворачивая Π΄Π²ΡƒΠΌΠ΅Ρ€Π½Ρ‹ΠΉ массив dp[i][j] Π² ΠΎΠ΄Π½ΠΎΠΌΠ΅Ρ€Π½Ρ‹ΠΉ массив dp[j], сохраняя Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΉ слой ΠΈ диагональноС Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ для ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π΅Π³ΠΎ слоя.

πŸš€ Π”Π΅Ρ‚Π°Π»ΠΈ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Π°

  1. ΠŸΡ€Π΅Π΄ΠΏΠΎΠ΄ΡΡ‡Π΅Ρ‚ частот символов:
    • Для ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΈΠ· 26 Π±ΡƒΠΊΠ² Π°Π»Ρ„Π°Π²ΠΈΡ‚Π° подсчитаСм количСство ΠΈΡ… Π²Ρ…ΠΎΠΆΠ΄Π΅Π½ΠΈΠΉ Π½Π° ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΈ Π² строках words. Π­Ρ‚ΠΎ ΠΏΠΎΠ·Π²ΠΎΠ»ΠΈΡ‚ Π½Π°ΠΌ быстро ΠΏΡ€ΠΎΠ²Π΅Ρ€ΡΡ‚ΡŒ, сколько Ρ€Π°Π· Π½ΡƒΠΆΠ½Ρ‹ΠΉ символ доступСн Π² ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΈ.
  2. Расчёт Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹ динамичСского программирования:
    • Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ массив dp, Π³Π΄Π΅ dp[j] Ρ…Ρ€Π°Π½ΠΈΡ‚ количСство способов ΡΠΎΠ±Ρ€Π°Ρ‚ΡŒ Ρ‚Π΅ΠΊΡƒΡ‰ΡƒΡŽ подстроку target с использованиСм ΠΏΠ΅Ρ€Π²Ρ‹Ρ… j-ΠΉ ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΉ ΠΈΠ· words
    • Для ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠΈ памяти сохраняСм Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΎΠ΄ΠΈΠ½ слой DP ΠΈ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΡƒΡŽ diag_val для хранСния диагонального значСния.
  3. ΠœΠΎΠ΄ΡƒΠ»ΡŒΠ½Π°Ρ Π°Ρ€ΠΈΡ„ΠΌΠ΅Ρ‚ΠΈΠΊΠ°:
    • Π§Ρ‚ΠΎΠ±Ρ‹ ΠΈΠ·Π±Π΅ΠΆΠ°Ρ‚ΡŒ пСрСполнСния i32, всС ΠΏΡ€ΠΎΠΌΠ΅ΠΆΡƒΡ‚ΠΎΡ‡Π½Ρ‹Π΅ вычислСния Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡŽΡ‚ΡΡ Π² i64 с ΠΏΠΎΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ ΠΏΡ€ΠΈΠ²Π΅Π΄Π΅Π½ΠΈΠ΅ΠΌ ΠΎΠ±Ρ€Π°Ρ‚Π½ΠΎ ΠΊ i32.

⏱️ Асимптотики

πŸ“„ Π˜ΡΡ…ΠΎΠ΄Π½Ρ‹ΠΉ ΠΊΠΎΠ΄

impl Solution {
    pub fn num_ways(words: Vec<String>, target: String) -> i32 {
        const MOD: i32 = 1_000_000_007;
        let word_length = words[0].len();
        let target_length = target.len();

        // Edge case: If the target is longer than the word length, it's impossible
        if target_length > word_length {
            return 0;
        }

        // Step 1: Build letter counters
        let letter_counters = Self::build_letter_counters(&words, word_length);

        // Step 2: Compute the number of ways using dynamic programming
        Self::compute_ways(&target, &letter_counters, word_length, target_length, MOD)
    }

    fn build_letter_counters(words: &Vec<String>, word_length: usize) -> [Vec<i32>; 26] {
        // Initialize a fixed-size array of 26 vectors using Default::default()
        let mut letter_counters: [Vec<i32>; 26] = Default::default();
        for vec in &mut letter_counters {
            *vec = vec![0; word_length];
        }

        for word in words {
            for (j, c) in word.bytes().enumerate() {
                letter_counters[(c - b'a') as usize][j] += 1;
            }
        }

        letter_counters
    }

    fn compute_ways(
        target: &String,
        letter_counters: &[Vec<i32>; 26],
        word_length: usize,
        target_length: usize,
        mod_val: i32,
    ) -> i32 {
        let target_bytes = target.as_bytes();

        // Initialize the DP array for the first target character
        let mut dp = vec![0; word_length];
        let first_char_counters = &letter_counters[(target_bytes[0] - b'a') as usize];

        dp[0] = first_char_counters[0];
        for j in 1..word_length {
            dp[j] = (first_char_counters[j] + dp[j - 1]) % mod_val;
        }

        // Process subsequent target characters
        for i in 1..target_length {
            let mut diag_val = 0;
            let current_char_counters = &letter_counters[(target_bytes[i] - b'a') as usize];

            diag_val = dp[i - 1];
            dp[i - 1] = 0;

            for j in i..word_length {
                let new_diag_val = dp[j];
                dp[j] = ((dp[j - 1] as i64 + diag_val as i64 * current_char_counters[j] as i64) % mod_val as i64) as i32;
                diag_val = new_diag_val;
            }
        }

        dp[word_length - 1]
    }
}