Изменение местами символов строки в скобках для достижения максимальной глубины строки H

Учитывая целое число H и строку s, которая содержит только символы ( и ) и правильно сформирована (каждая открывающая скобка имеет соответствующую закрывающую скобку, пары скобок правильно вложены), верните целое число, минимально указывающее, сколько скобок вам нужно перевернуть. получить действительную строку, глубина которой не превышает H.

Глубина строки в данном случае — это максимальный уровень вложенности скобок. Например, строка (()(())) имеет глубину, равную 3.

В целом, я думаю, мы можем применить жадный подход — просто перебирать строковые символы и при этом сохранять переменную, в которой хранится текущая глубина строки. Если в какой-то момент оно окажется выше H, измените текущую скобку. Кроме того, в случае, если глубина будет ниже нуля, также переверните символ.

Это код, который я написал, и он работает отлично:

#include <iostream>
#include <vector>

using namespace std;

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

    int n, H;
    cin >> n >> H;

    string s;
    cin >> s;

    int depth = 0;
    int ans = 0;
    for (int i = 0; i < s.size(); ++i) {
        if (s[i] == '(' && depth == H) {
            s[i] = ')';
            ++ans;
        }
        else if (s[i] == ')' && depth == 0) {
            s[i] = '(';
            ++ans;
        }

        if (s[i] == '(')
            ++depth;
        else
            --depth;
    }

    cout << ans;
}

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

Во-первых, в какой-то момент мы придем к случаю, когда глубина превысит H. Здесь мы переворачиваем открывающую скобку на закрывающую. Предположим, мы сделали это по индексу i (при повторении s). Благодаря этой модификации у нас будет закрывающая скобка, которой в будущем не будет соответствующей скобки (некоторый индекс j где j > i). Может случиться так, что после этого глубина станет ниже нуля (поэтому мы поменяем закрывающую скобку на открывающую), но это не обязательно (и в итоге мы получим недопустимую строку).

На этом мои рассуждения заканчиваются. Для меня это кажется недействительным, но если код работает, значит я что-то пропустил.

Итак, как я могу быть уверен, что после выполнения всех этих операций я получу действительную строку?

Не связано, но выкиньте первые три строки функций main. Они всего лишь часть карго-культа , который обычно никогда не используется в реальном мире.

Some programmer dude 07.07.2024 19:42

Также прочтите В чем проблема с «использованием пространства имен std;»? и, пожалуйста, не делайте карго-культовых вещей вроде «ios_base::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);», которые никогда не пройдут проверку кода в любой профессиональной среде и (если вы действительно не знаете, что делаете) просто приведут к ошибкам.

Jesper Juhl 07.07.2024 19:42

Но я не могу понять, как я могу быть уверен, что окончательная строка (после изменения местами определенных символов) всегда будет допустимой (круглые скобки вложены правильно и т. д.). Запустите тысячи случайных тестовых случаев с известными ответами, а затем сравните ваши ответы. на известные ответы. Вы просите нас стать вашим фаззинговым двигателем

PaulMcKenzie 07.07.2024 19:47

@Someprogrammerdude Это не совсем разумный совет. В случае проблем с большим объемом ввода программы с заданной временной сложностью могут превысить ограничение по времени без этих строк для ускорения ввода. От их удаления нет никакой пользы, хотя я согласен, что отвязывание cin не так уж и необходимо (и cout в любом случае не привязано по умолчанию).

Unmitigated 07.07.2024 19:57

@Unmitigated Я никогда не работал над каким-либо реальным профессиональным программным проектом, где синхронизация потоков ввода/вывода C и C++ когда-либо имела какое-либо значение. Но я работал над базами кода, куда какие-то придурки добавляли такие вещи, как в код ОП, и это вызывало настоящие ошибки, которые стоили реальных денег. Это «конкурентное кодирование», от которого просто нужно избавиться. Конечно, это существует по какой-то причине, и есть (несколько) случаев, когда это может иметь смысл, но это территория экспертов, и это явно не то, где мы находимся в настоящее время.

Jesper Juhl 07.07.2024 20:02

@JesperJuhl Пока компании задают технические вопросы, которые представляют собой упрощенные вопросы конкурентного программирования, это вряд ли исчезнет. А поскольку это проблема конкурентного программирования, удаление этих строк на самом деле не помогает (и может существенно ухудшить решение).

Unmitigated 07.07.2024 20:05

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

Igor Tandetnik 08.07.2024 04:08

@IgorTandetnik "это единственный выход" нет.

n. m. could be an AI 08.07.2024 06:34
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
8
106
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Перефразируя вопрос, я думаю, вы просите доказать правильность вашей программы, а не «написать фаззер», как предлагалось в некоторых комментариях.

Вы можете думать о допустимой строке скобок следующим образом: для вашей строки скобок s длины n мы можем представить ее с помощью последовательности a. Пусть ai = +1, если i-й символ — (, и пусть ai = -1, если i-й символ — ). Теперь, когда s является допустимой последовательностью скобок тогда и только тогда, когда ∑ i=0k ai ≥ 0 для 0 ≤ k < n и что ∑ i=0n-1 ai = 0. (Подробнее см. Каталонское число и путь Дьюка.)

Таким образом, изменение ( на ) эквивалентно изменению +1 на -1. Следовательно, предположим, что вы меняете al = +1 на al = -1 (от ( до )) и am = -1 на am = +1 (от ) до () для некоторого l < m, тогда ∑ i=0k a i будет неизменным для всех k до l и после m. Что касается сумм между l и m, поскольку ваша программа по сути ищет первое m, где сумма ∑ i=0m ai становится отрицательной после того, как вы изменили ( на ) в точке l, они определенно не отрицательны. Следовательно, строка в квадратных скобках, созданная вашей программой, всегда действительна.

Что касается вашего беспокойства: «из-за этой модификации у нас в будущем будет закрывающая скобка, у которой не будет соответствующей скобки» и «может случиться так, что после этого глубина станет ниже нуля», на самом деле это не будет проблемой. Рассмотрим следующий пример:

( ( ( ) ( ) ) )

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

( ( ) ) ( ) ( )

Теперь обратите внимание на жирные скобки ниже, ранее это была пара скобок: ( ( ( ) ( ) ) )

Но самая левая скобка теперь будет выглядеть следующим образом:

( ( ) ) ( ) ( )

Поэтому вам не нужно беспокоиться о том, какие именно скобки соединены в пары, а достаточно лишь о том, чтобы каждая закрывающая скобка была в паре с некоторой открывающей скобкой.

Другие вопросы по теме