главная новое лучшее написать
3

На этот раз наша задача выглядит заковыристо - 2982. Find Longest Special Substring That Occurs Thrice II.

📝 Описание задачи

Необходимо найти длину самой длинной "особенной" подстроки, которая встречается в строке хотя бы три раза. Если такой подстроки нет, вернуть -1.

"Особенная" подстрока — это подстрока, содержащая один и тот же символ.

😊 Идея

Отбросим сразу идею перебора всех возможных подстрок (что крайне неэффективно).
Вместо этого мы сосредоточимся на последовательных участках каждого символа.
Для каждого символа достаточно рассмотреть только длины его подряд идущих сегментов, и это позволит нам эффективно вычислить искомый результат.

🚀 Подход

  1. Перебираем все строчные английские буквы ('a' до 'z').
  2. Для каждой буквы:
    • Находим все её подряд идущие сегменты (например, для 'aaaabbaab' сегменты: [4,2], [2,1] для 'a' и 'b').
    • Сортируем длины сегментов по убыванию.
    • Вычисляем максимальную возможную длину "особенной" подстроки, встречающейся как минимум три раза, используя метод get_triple_length.
  3. Поддерживаем глобальный максимум для всех символов.
  4. Если не найдено ни одной подходящей подстроки, возвращаем -1.

⏱ Сложность по времени

🗂 Сложность по памяти

Исходный код решения

impl Solution {
    pub fn maximum_length(s: String) -> i32 {
        let mut best = -1;
        let data = s.as_bytes();

        // Iterate over all lowercase English letters
        for ch in b'a'..=b'z' {
            let segments = Solution::get_segments(data, ch);
            if !segments.is_empty() {
                best = best.max(Solution::get_triple_length(&segments));
            }
        }
        best
    }

    // Function to calculate the maximum length of a segment that occurs at least three times.
    fn get_triple_length(segments: &Vec<usize>) -> i32 {
        let mut max_length = (segments[0] as i32) - 2;
        if segments.len() > 1 && segments[1] >= (max_length + 1) as usize {
            max_length += 1;
            if segments.len() > 2 && segments[2] >= (max_length + 1) as usize {
                max_length += 1;
            }
        }

        if max_length > 0 {
            max_length
        } else {
            -1
        }
    }

    // Function to find lengths of consecutive characters in the string for a given character.
    fn get_segments(data: &[u8], ch: u8) -> Vec<usize> {
        let mut segments = Vec::new();
        let mut i = 0;

        while i < data.len() {
            if data[i] == ch {
                let mut length = 1;
                // Count consecutive characters matching `ch`
                while i + length < data.len() && data[i + length] == ch {
                    length += 1;
                }
                segments.push(length);
                i += length; // Skip the rest of the segment
            }
            i += 1;
        }

        // Sort segments in descending order
        segments.sort_by(|a, b| b.cmp(a));
        segments
    }
}

2 finder 10-12-2024

А в Rust нет аналога std::nth_element? Можно было бы от klogk избавиться (не то чтобы это на практике очень важно, конечно)

ответить
2 leetcoder 10-12-2024

Ты прав, так можно!
В Rust - этот метод называется немного по уродски, но есть: select_nth_unstable.
Есть и проще метод - нужно просто 3 итерации BubbleSort'a прогнать и они гарантируют 3 наибольших значения на своих местах.

ответить
1 leetcoder 10-12-2024

Ну и кроме того существует стандартный олимпиадный способ - трекать наибольшие значения в двоичной куче на 3 элемента. Тут и время O(k) (с константой порядка log 3 на каждую операцию) и дополнительная память - O(1).

ответить
2 leetcoder 10-12-2024

Кстати, используя кучу 3 максимумов, можно упростить код и выкинуть отдельный метод get_triple_length(...).
Достаточно просто с каждый найденным сегментом длины k одновременно в кучу добавлять k-1 и k-2.
Тогда наименьший элемент в куче после всех операций и будет ответом.

// Function to find the maximum special substring length for a given character.
fn solve_char(data: &[u8], ch: u8) -> i32 {
    let mut segments = BinaryHeap::new();
    let mut i = 0;

    while i < data.len() {
        if data[i] == ch {
            let mut length = 1;

            // Count consecutive characters matching `ch`
            while i + length < data.len() && data[i + length] == ch {
                length += 1;
            }

            // Push adjusted lengths into the heap
            for k in 0..3 { 
                if length <= k {break}
                if segments.len() >= 3 && Reverse(length - k) >= *segments.peek().unwrap() {break} 
                segments.push(Reverse(length - k));
            }

            // Maintain only the top 3 lengths
            while segments.len() > 3 {
                segments.pop();
            }

            i += length; // Skip the processed segment
        }
        i += 1;
    }

    // Return the third-largest value if we have 3 elements, otherwise -1
    if segments.len() == 3 {
        match segments.pop().unwrap() {
            Reverse(l) => l as i32,
        }
    } else {
        -1
    }
}
ответить