Ответы пользователя по тегу Алгоритмы
  • Каким образом можно сделать рекурсивную функцию кластеризации объектов?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    У вас есть граф, где вершины - теги, и есть ребра между ними. Если брать ребро там, где два тега не совместимы, то у вас задача поиска клик.

    Во первых, тут есть неоднозначность. Может быть так что теги A,B,C попарно несовместимы, но заодно попарно несовместимы теги A,D,E - куда относить тег A, в какую группу?

    Поиск самой большой группы - сложная задача тут нет простых алгоритмов. Полный перебор и всякое тому подобное тут работает.

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

    Еще есть Алгоритм Брона — Кербоша. Он перебирает все максимальные клики.
    Ответ написан
  • Preimage - атака нахождения прообраза. Теория + практика. Пофантазируем?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Подсчитайте, сколько различных текстов подходят под ваш паттерн (каждый ? дает 10 вариантов - всего 10^k, где k - количество ? в шаблоне).

    Посмотрите, сколько ваша видео карточка выдает хэшей. Беглый гуглеж дает что-то порядка 3Ghash/s Поделите 10^k на 3*10^9 - получите максимальное время в секундах. Можно ожидать, что в среднем понадобится половина этого времени.

    Если вы используете кластер, то вам понадобится (сколько времени вы насчитали выше)/(сколько времени вы готовы ждать) одинаковых компов в кластере. Если это обычные юзеры, то нельзя сильно нагружать и понадобится в 10 раз больше компов.

    Если я не сбился в расчетах, то вам понадобится 5-6 лет на одной карточке, чтобы подобрать текст. Если вы готовы ждать месяц, то вам понадобится 600 компов в кластере.
    Ответ написан
    1 комментарий
  • Что не так с алгоритмом создания максимального числа из исходного, используя двоичную СС?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Потому что нельзя просто искать максимальное скопление единиц. Их может быть несколько - какое из них выбирать зависит от того, что идет после них. Например вот такое число в двоичной системе: 1001110100101110110.

    Тут есть 2 куска 1110. Но за первым идет "010", а за вторым "011". Начинать со второго - выгоднее.

    В этой задаче очень маленькие ограничения - можно полностью перебирать все возможные числа и брать максимальное. Можно даже не переводить в двоичную систему, а воспользоваться битовыми операциями.
    Когда вы узнали, сколько битов в числе, то самый младший бит x можно получить как x&1. Сдвинуть все биты числа на одну позицию вправо - это x >> 1. При этом младший бит пропадает. Чтобы вставить новый бит b слева нужно сделать x | (b << k) - тут k - номер позиции этого бита, считая с 0.

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

    И так, для развития: Если бы ограничения были слишком большие (число в десятки тысяч бит), то тут пришлось бы применять умные строковые алгоритмы. Это была бы задача на поиск лексикографически максимального циклического сдвига. Решается с помощью суффиксного дерева, суффиксного массива или суффиксного автомата.
    Ответ написан
    2 комментария
  • Как вывести определенное значение в центр массива при его разной размерности?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Edit:

    Раз задача построить концентрические вложенные квадраты, то есть 2 подхода.

    Простой для понимания, но менее эффективный (но все-равно отлично быстрый для смешных ограничений в задаче) - напишите функцию, которая в двумерном массиве рисует квадрат заданного размера вокруг центра. Это тупо 4 последовательных, не вложенных цикла. Каждый рисует одну сторону квадрата. Тупо цикл до n, где n - длина стороны квадрата. Там одна координата фиксирована, а другая пробегает вдоль стороны. Надо чуть-чуть подумать, и составить формулы, какие строки и столбцы будут закрашены.

    Заведите двумерный массив, заполните его пробелами, и потом циклом от n%2 до n с шагом 2 рисуйте квадраты.

    Второй, более эффективный, подход - это немного подумать. Возьмите клетчатый лист, или в редакторе каком-либо нарисуйте ответ для n=9,10. Подумайте над паттернами. Первая строка будет всегда из n #. Вторая будет #, n-2 " ", #. Следующая "# #...# #" и так далее.

    Одни строки будут иметь в середине отрезок из "#" какой-то длины, а по краям заданное количество чередующихся "#" и " ". Соседние строки будут содержать в середине отрезок из пробелов, а вокруг чередующиеся решетки и пробелы. По номеру строки можно весьма просто вычислить длины среднего отрезка и чередующихся кусков. Соответственно можно вывести ответ сразу же не формируя его в массиве. Выводите чередующиеся "# " нужное количество раз. Потом выводите "#" или " " нужное количество раз. Потом выводите " #" нужное количество раз. Это один внешний цикл, несколько тупых формул, три вложенных последовательных цикла с выводом.

    Подсказка для формулы - имеет значение, как близко строка к середине массива и четность n. Расстояние до середины можно получить как abs(n/2 - i)
    Ответ написан
    4 комментария
  • Как реализовать распознавание кривых на canvas?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Готовых решений я не знаю, но есть следующие идеи.

    Во-первых, научимся аппроксимировать одной кривой. Первая и четвертая точки нам известны - это первый и последний пиксель. координаты двух других опорных точек кривой Безье - это четыре неизвестных.

    Составим функцию ошибки f(x1, y1, x2, y2) = (X(i/n)-x_i)^2+(Y(i/n)-y_i)^2. Тут x_i, y_i - координаты i-ого пикселя в нарисованной кривой, пронумерованные от 0 до n, X(t), Y(t) - координаты точки на кривой Безье - линейные комбинации x1, x2 и y1, y2 с известными вычисляемыми от t коэффициентами. Это фактически сумма квадратов расстояний от каждого пикселя до неизвестной кривой. Ее можно минимизировать подобно методу наименьших квадратов - считайте производную по всем переменным, приравнивайте к 0. Получите 4 неизвестные и 4 линейных уравнения. Можно вообще на бумажке руками методом Краммера решить, а можно алгоритмом Гаусса подсчитать.

    Тут есть допущение, что i-ый пиксель будет аппроксимироваться t=i/n точкой на кривой. Вообще говоря, это не гарантированно, но в качестве некой грубой меры оптимальности подойдет. Может вообще отлично работать будет и так, я не знаю. Поэксперементируйте. Но еще можно потом честно искать ближайшую точку на кривой к заданному пикселю как оптимум полинома 6-ей степени от t, когда все опорные точки кривой фиксированы. Тут надо брать производную, находить ее нули и среди них брать лучшее t. Чтобы найти нули полинома 5-ой степени можно рекурсивно брать производную, находить нули полинома меньшей степени, и потом на каждом монотонном отрезке искать пересечение с OX бинарным поиском. Или использовать метод Ньютона. Это давно решенная задача - должно быть куча готовых решений.

    Когда вы так научились считать расстояние от кучки пикселей до кривой Безье, можно локально градиентным спуском по 4-м координатам x1, x2, y1, y2 улучшать эту правильную метрику с оптимума полученного грубой метрикой.

    Вот мы и умеем аппроксимировать кучку пикселей одной кривой. Заодно мы получаем метрику близости кривой к пикселям. Но надо сделать ее не зависящей от длины - поэтому складывайте квадраты расстояний и делите на количество пикселей.

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

    Это эмпирический алгоритм и он много где жадный. Нет никакой гарантии, что он даст математически оптимальный результат, но есть много шансов, что он даст достаточно хороший результат.
    Ответ написан
    2 комментария
  • Где ошибка в алгоритме создания плоской выпуклой фигуры?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    На будущее: пишите вместе с задачей вашу идею решения.

    Уберите ненужные переменные. Еще, зачем у вас там i % columns?

    Похоже, надо в output.txt выводить, а не в stdout.

    Еще, советую выводить перевод строки всегда, и не пропускать его на последней строке вывода.

    Чтобы не было проблем с пробельными символами - читайте в двух вложенных циклах до rows и columns по одному char (а не string до eof). Это и ввод упростит и сделает его более безопасным ко всяким артефактам в тестах.
    Ответ написан
  • Сколько шагов в нахождении наибольшего делителя в алгоритме Евклида?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    n=5, m=1. первый шаг - поделить, осталось n=1, m=0. Второй шаг - проверить, заметить 0 и остановиться.

    n=5,m=3, первый шаг - поделить. осталось n=3, m=2. Второй шаг - опять делим, остается m=2, n=1. Третий шаг - делим, остается n=1, m=0, четвернтый шаг - видим 0, остановились.

    для n=m=5 ответ 1, возможно, потому что в алгоритме стоит проверка m==0 || m == n.
    Ответ написан
  • Каким образом получить такой набор? Какие методы использовать?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Это похоже на задачу set cover. Легких алгоритмов тут нет. Только полный перебор с отсечениями. Еще всякие методы отжига и эволюционные алгоритмы могут найти хорошее решение. Ну и, задачу можно переформулировать в виде integer linear programming и решать какой-то из существующих библиотек.

    Если количество слов маленькое (типа 25-30), то можно решать динамическим программированием по маске покрытых слов.

    Если вам нужно не оптимальное решение, а достаточно хорошее, то могут сработать всякие жадности, типа брать документ, который покрывает наибольшее количество непокрытых слов.
    Ответ написан
    Комментировать
  • Как быстро проверить, является ли некоторое огромное число (от 100 знаков) квадратом целого числа?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Можно попробовать вычислить корень быстрым алгоритмом. Но там сложно. Гуглите Karatsuba square root. Есть открытые реализации. Есть еще какой-то адский метод через быстрое преобразование Фурье, попробуйте погуглить и его.

    Более простой в реализации, но менее быстрый метод вычисления корня - бинарный поиск. Храните l, r, l^2, r^2 и lr. По этим числам можно вычислить m=(l+r)/2, m^2, m*l, m*r без длинных умножений, а только складывая длинные числа и деля на 2. Вам надо поддерживать, чтобы l^2 <= n <= r^2. Изначально можно сделать l=1, r=10^51 (или больше - половина длины входного числа + немного, чтобы точно квадрат был больше n), потом делить отрезок пополам и отбрасывать ненужную половину.

    Еще есть вероятностный метод через символ Лежандра/Якоби. Это будет самым быстрым методом.

    Это как смотреть на последнюю цифру. Квадраты могут давать там 0, 1, 4, 9, 6, 5. Но нет ни одного квадрата, который оканчивался бы на 2. Т.е. если число заканчивается на 2, то можно сразу говорить, что это не квадрат. Это мы взяли остаток от деления на 10 (последняя цифра) и посмотрели, какие из них хорошие. Вот символ Лежандра - это такая проверка для модуля по любому простому числу (а не 10).

    Если брать некоторое простое число p, то n может быть квадратом, только если символ Лежандра (n/p) - равен 1 или 0 (По научному: n - должно быть квадратичным вычетом).

    Если брать небольшие (<64-битные) простые числа, то можно за линию считать n%p и потом вычислять символ Лежандра (n%p/p) по алгоритму через символ Якоби за O(log(p)^2). Когда подсчитали символ Лежандра и если он -1, то n - точно не корень.

    Тут проблема в том, что это необходимый, но недостаточный критерий - если для какого-то p вы получили -1 - то это точно не квадрат. Но возможно можно подобрать такое число, что все ваши тесты дадут 1, а оно не квадрат. Поэтому надо брать много простых чисел. Скажем, 20. Желательно еще числа брать достаточно большими. Но их не надо искать каждый раз, можно захардкодить. Грубая прикидка говорит, что вероятность ошибки такого алгоритма 2^(-количество простых чисел).

    Т.е. берете много простых чисел. Считаете для каждого n%p выполняя деление большого числа на короткое (один проход по массиву цифр). Потом считаете символ Лежандра. Если получили где-то -1 - то точно не квадрат. Иначе - скорее всего квадрат.

    Можно совместить вероятностный тест и вычисление корня. Сначала проверьте парочку простых чисел на символ Лежандра для отсечения точно не квадратов. Еще проверку последней цифры можно сделать, это очень дешево. Если не отсеклись, то считайте корень. Так будет всегда работать правильно но будет быстрее работать в некоторых случаях.
    Ответ написан
    Комментировать
  • Перевод фото таблицы в матрицу из 0 и 1?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Нет тут никаких алгоритмов. Тупо пройдитесь по изображению двумя циклами и заполните соответствующую ячейку нулем или еденицей.

    На изображении квадратики размера 2x2, между ними один пиксель пустой. И по границам еще пустые пиксели. Если обрезать 1 пиксель справа и снизу, то получится, что каждой ячейке соответствует квадрат 3x3: или целиком белый, или с 4-мя черными пикселями в правом нижнем углу.

    Соответственно, достаточно проверять пиксель с координатами 3*i+1, 3*j+1 для получения значения ячейки [i][j] (все индексы с 0).

    Т.е. в вашем языке программирования получаете изображение в виде матрицы пикселей, потом двумя циклами до 100 заполняете все ячейки матрицы - смотрите на нужный пиксель и, если он не белый, ставите 1.

    Если изображение с артефактами сжатия, то можно брать среднее между 4-мя пикселями 3*i+x, 3*j+y (x,y=1..2) по всем трем компонентам (RGB) и смотреть, что оно сильно отличается от черного. Для этого делаете еще 2 вложенных цикла по x и y, там прибавляете значение цветов пикселя в переменную, потом делите на 12 и сравниваете, допустим, с 200. Если меньше - это черный квадрат, если больше - то белый.
    Ответ написан
    2 комментария
  • Какой алгоритм сортировки односвязного списка?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Для списка отлично подойдет merge sort. В отличии от массива - для списков не понадобится второй массив для хранения временных результатов (вообще говоря, можно и массив без него сортировать, но получается медленнее и слишком сложный алгоритм).
    Ответ написан
    Комментировать
  • Как можно найти все пути между вершинами графа networkx?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Я предполагаю, что вам нужны только простые пути, без самопересечений по вершинам. Иначе путей может быть бесконечно много.

    Готового решения нет, потому что это довольно редкая задача - получить все пути. Кстати, даже без самопересечений путей может быть экспоненциально много. Уже для для 15 вершин есть тривиальный граф, в котором почти 100 миллиардов простых путей между любыми двумя вeршинами.

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

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

    Что-то типа такого. Я не питонист, так что возможно с ошибками, но идея должна быть понятна.

    def dfs(v, end, graph, path, visited):
      if v == end:
        print(path)
        return
      for u in graph.neighbours(v):
        if u not in visited:
          path.append(u);
          visited.add(u);
          dfs(u, end, graph, path, visited)
          path.pop();
          visited.remove(u);
    
    // вызывать dfs(start, end, graph, [], set())
    Ответ написан
    Комментировать
  • По какой формуле вычислить минимальное количество раз для установки шага диапазона для вычисления?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Звучит, как бинарный поиск. Изначальный шаг - половина длины массива (128).
    Следующий, половина от этого и так далее. Так будет максимум 8 проверок всего.

    Если же надо делать обязательно +1 вторым шагом, то можно вычислить максимальное количество проверок, если первый шаг - X, а длина массива - N.

    Первых проверок будет максимум - floor(N/X), вторых (с шагом +1) - X-1.

    Можно примерно подсчитать в вещественных числах и попытаться минимизировать f(x) = N/X+X.

    Можно найти производную f'(x) = 1-n/x^2. Ноль у этой производной при x=sqrt(n).

    Отсюда получается, что примерно sqrt(256)=16 - искомая длина. Максимальное число проверок - 32.
    Ответ написан
    3 комментария
  • Как найти рекурсивным способом эйлеров путь в графе, из какой вершины начинать?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Эйлеров цикл существует в графе, только если входная степень каждой вершины равна ее выходной степени.

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

    Это элементарно доказать - путь в каждую вершину входит сколько-то раз и столько же раз выходит. Исключение - только начальная и конечная вершина, если они не совпадают.

    Соответственно, вам надо подсчитать степени всех вершин, проверить что все хорошо (максимум одна с in==out+1 и одна с in==out-1) и запустить или от любой или от той, у которой исходящих ребер больше.
    Ответ написан
    Комментировать
  • Какой алгоритм для вычисления оптимальной задержки для API и сообщения её ПО пользователя, чтобы не генерировать лишнюю нагрузку?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Это известная и сотню раз решенная задача. Гуглите "exponential backoff" или "Экспоненциальная выдержка".
    Ответ написан
  • Какие есть алгоритмы сортировки и поиска с помощью деревьев?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Самое быстрое и простое - сортировка кучей.

    Теоретически, можно любое search tree использовать, но оно будет медленее, потому что структуры сложнее.
    Ответ написан
  • Где и как используют деревья в программировании?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Само по себе тупо дерево весьма бесполезно. Но как базовая организация данных она встречается много где. Если на него навесить какие-то дополнительные свойства и поддерживать их, то получаются классные штуки.

    Балансированные бинарные деревья поиска - очень популярная структура данных. Используется где угодно, если вам нужно хранить множество или ассоциативный массив чего-либо и менять произвольные элементы.

    Еще деревья часто используются в алгоритмах на строки. Есть такая структура - бор (trie) - позволяет эффективно хранить кучу строк и искать: есть ли такая строка в структуре. Более продвинутые алгоритмы типа Ахо-Корасика, Укконена тоже строят некоторое дерево с дополнительными фишками и позволяют делать крутые вещи, типа искать кучу шаблонов в тексте разом, или моментально находить самую часто встречающуюся подстроку задонного размера.

    Куча (heap) используется для сортировки а так же реализации приоритетных очередей.

    Далее, деревья в смысле графов тоже используются, например, в сети. Маршрутизаторы строят остовное дерево и раздают команды по его ребрам.
    Ответ написан
    Комментировать
  • Какую структуру данных выбрать для подсчета элементов?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Вам нужен std::map или std::unordered_map. Самому писать структуру данных не надо.

    Первое будет деревом поиска, второе - хештаблицей.
    Дерево поиска гарантированно будет работать стабильно быстро. Хеш таблица будет работать быстрее, но если вам не повезет, то может работать очень долго. Но предпочтительнее, на мой взгляд, таблица.

    В качестве ключа используйте пару {Тип транспорта, номер маршрута}. В качестве значения - счетчик остановок.

    Еще вам нужен еще один map из тип транспорта-> std::set или std::unordered_set номеров маршрутов.

    Для построения структур один раз приходитесь по всем остановкам и увеличивайте счетчик в первой структуре. Добавляйте маршрут к транспорту во второй структуре.

    Для поиска ответа пройдитесь циклом по всем элементам set из второго map - это все маршруты. Смотрите в первом map'е сколько остановок у этого маршрута и выбирайте максимум.
    Ответ написан
    2 комментария
  • Алгоритм работает не правильно?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Бинпоиск поддерживает текущий отрезок, в котором возможно есть искомый элемент. Смотрит на середину и отбрасывает одну из половин отрезка.

    Вы же как-то пытаетесь реализовать это только лишь с переменной mid. Если надо идти влево, то вы делите mid пополам, как будто бы текущий отрезок от начала массива (Но это не всегда так: после первого же шага вправо начало массива будет уже выкинуто из расмотрения), Потом, при переходе вправо, у вас какой-то бред написан.
    Math.floor((arr.length - mid) / 2) - это что вообще должно делать? Если mid=9, length = 10, вы вообще уйдете в начало массива, хотя должны идти вправо. Если вы хотели взять середину между mid и length, то там должен стоять "+" внутри.

    Но так все-равно не получится сделать. Заведите 2 переменные l и r, как во всех реализациях бинпоиска, считайте mid как середину отрезка, и при выбрасывании одной из половин просто переписывайте l или r на mid+1 или mid-1 (потому что сам mid элемет вы уже рассмотрели и он точно не нужен).
    Ответ написан
  • Как исправить скобочную последовательность?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Если это задача такая, то я предполагаю, что там надо удалить минимальное количество скобок, чтобы последовательность стала правильной. Можно только удалять скобки, потому что вместо любого добавления скобки, можно просто вторую пару удалять. Количество действий не изменится. Правда, в ваших примерах оно удалит все скобки.

    Тут можно решать динамическим программированием. Пусть F(l,r) - минимальное количество операций удаления, чтобы сделать из строки с l по r правильную скобочную последовательность.

    База - если l..r - пустая строка - ответ 0.
    Иначе надо рассматривать варианты, что будет с последним символом. Если в конце стоит открывающая скобка, то ее надо удалить - других вариантов нет: F(l,r) = 1+F(l,r-1).

    Если же там закрывающая скобка, то есть 2 варинта: или этот символ удаляем, или берем в ответ. В первом варианте ответ такой-же, как выше. Во втором - надо перебрать, а какой же символ в строке будет открывающей скобкой для данной. Пусть это символ i (там должна стоять открывающая скобка того же типа). Тогда ответ F(l,i-1)+F(i+1,r-1) - ведь части перед парой скобок и внутри их должны тоже быть правильными последовательностями.
    Из всех вариантов надо выбрать минимальный - это и будет ответ для текущего состояния.

    Если хотите восстанвливать саму последовательность, то надо при сохранении минимума еще и сохранять в отдельном двумерном массиве - какой именно из вариантов был выбран (дропнуть последний символ, или какой символ взять ему в пару).

    Ответ к задаче - F(1,n) - для всей строки.

    Это решение потребляет O(n^2) памяти и занимает O(n^3) времени.
    Ответ написан
    2 комментария