• Как доказать равенство множеств?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Формально по законам логики и определениям.
    Что значит какой-то элемент принадлежит правому множеству?
    x in A\(A\B), тогда и только тогда, когда x in A И x not in (A\B),
    Чтобы вторая половина после И выполнялось, надо наложить отрицание не x in (A\B) что тоже самое, что (x in A И x not in B). По законам логики это будет (x not in A ИЛИ x in B). добавляем к этому первую часть и раскрываем скобки:

    x in A И (x not in A ИЛИ x in B) = (x in A И x not in A) ИЛИ (x in A И x in B).

    Первая часть - всегда ложна и сокращается, остается только (x in A И x in B), что по определению x in A и B. Мы везде использовали только эквивалентность и из определения, что элемент принадлежит правому множеству, получили определение, что элемент принадлежит левому множеству. Значит эти 2 множества равны.
    Ответ написан
    Комментировать
  • Наиболее подходящий алгоритм для поиска цены?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Чтобы сделать бинарный поиск, вам нужен массив объектов с ценами и названиями. Что-то вроде
    [1=>{cost:1, name:"name1"} .... 117=>{cost:55233, name:"name555"}]
    (не уверен, что правильно описал на js).

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

    Можно поискать какие-нибудь js библиотеки с нормальным ассоциативным массивом, который это умеет.
    Будет также работать за логарифм.
    Ответ написан
    Комментировать
  • Как найти вектор сигнала в плоскости?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Похожую задачу решает GPS (если забить на всякие физические эффекты).

    У вас будет 3 переменные - координаты источника сигнала (x, y) и время отправки сигнала (t).
    Вам даны координаты трех точек (x_i, y_i, i=0..2), три времени получения сигнала (t_1, t_2, t_3) и скорость сигнала (v).

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

    Уравнения, что сигнал проходит заданное расстояние за заданное время с известной скоростью:

    (x_i-x)^2+(y_i-y)^2 = ((t-t_i)*v)^2

    Можно время считать не в секундах, а в 1/v, тогда уравнения чуть упрощаются (коэффициент перед t^2 везде 1, а не v^2).

    Можно решать аналитически. Вычтите первое уравнение из двух остальных. У вас получится 2 линейных уравнения с тремя неизвестными x, y, t. Считайте, что t - это константа и решите уравнения относительно x и y (Через определители, или метод Краммера). У вас будет какая-то линейная зависимость x и у от t (большие формулы, да). Можно упростить вычисления, если cначала записать уравнения в виде A1x+B1y=C1+D1t.

    Потом подставьте эти зависимости в первое уравнение и у вас будет квадратичное уравнение на t.

    Решите его. Подставьте t в известные уравнения для x и y - и вот ваш центр (заодно вы знаете, когда был отравлен сигнал).

    Из двух значений t, одно будет в будущем (положительное), его надо будет отбросить.
    Ответ написан
    2 комментария
  • Где у меня ошибка?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Проблема в том, что числа повторений в задаче ОЧЕНЬ большие.
    Правильное решение должно не строить строку, а хранить ее в виде пар (символ, количество), как в RLE.
    Самое быстрое решение - хранить список таких пар (или 2 списка). Но проще, и должно проходить по времени другое решение: Можно просто хранить массив этих пар (или 2 массива, один для символов, другой - для количества повторения).

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

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

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

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

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    | - это побитовое ИЛИ. & - побитовое И. Т.е. каждый разряд отдельно обрабатывается и, в случае ИЛИ, если хоть одно число имеет единицу в данном разряде, то и результат будет 1. Соответственно, 00001 | 00010 = 00011.

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

    Чтобы проверить, что бит в числе установлен, надо взять побитовое И и убедиться, что результат не 0 (он может быть либо 0 либо какой-то степенью двойки.
    Ответ написан
    Комментировать
  • Как найти n+1 ое простое число за минимальное время?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Есть - перебирайте следующее число, начиная с p_(n-1)+1, потом проверяйте делимость на все простые числа, пока они меньше корня и текущего числа.

    Можно применить следующие оптимизации - начинать с p_(n+1)+2 и идти с шагом 2 (только нечетные числа). Перебирать числа только вида 6k-1 и 6k+1 (k изначально floor(p_(n-1)+3) / 6). Вместо высчитывания корня, в коде должно быть возведение в квадрат - что-то вроде while (i < n && p[i]*p[i] <= x) ... i++;.

    Это если вам уже дан список предыдущих простых чисел. Если же вы думаете над шагом алгоритма, который находит первые n простых чисел или все простые до какого-то K, то быстрее будет использовать решето эратосфена.
    Ответ написан
    Комментировать
  • Скидка на чек, алгоритм разбиения?

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

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

    Т.е. в вашем примере скидка должна быть 86.1/1086.1. В позиции1 цена будет 9800*(1-86.1/1086.1) = 9023.11 ~ 9024.

    Для этого можно сделать
    item.price = item.price - floor(item.price * discount / total )


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

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

    Что-то вроде:
    leftover = ... # сколько осталось скинуть с чека
    foreach item in items:
      if leftover == 0: break
      if item.count > leftover:
        new_item = item
        new_item.count = item.count - leftover
        item.count = leftover
        item.price -= 1
        items.add(new_item)
        break
      else:
        item.price -= 1
        leftover -= item.count


    Есть, правда, один крайний случай. Что делать, если все товары стоят по 0.01 изначально? Можно ли скидывать товар до 0? Если можно, то этот алгоритм работает. Если нет, то такой чек вообще никак нельзя урезать.
    Ответ написан
    Комментировать
  • Какие существуют практические примеры применения арифметики колец и полей?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    В криптографии просто куча применений. Всякие RSA, DH, эллиптические кривые - это все основано на свойствах некоторых особых конечных полей.

    Но мне нравится такой пазл: есть таблица из лампочек-кнопок n x m. Какие-то горят, какие-то - нет. Можно нажать на какую-то лампочку и она переключится. Но вместе с ней переключатся и 4 соседние лампы (если нажали на угловую кнопку - то только 2). Нужно погасить все лампы за минимальное количество нажатий. Как ее вообще решать без полного или частичного перебора? Важно заметить, что 2 раза нажимать на лампу бессмысленно, потому что эти 2 нажатия просто отменят друг друга. Еще не важно, в каком порядке нажимать на лампы. Конечный результат будет одинаковый.

    А дальше, подключается математика! Введем переменные x_ij - сколько раз мы нажимаем на лампочку в позиции i, j. Эти переменные - это элементы поля по модулю 2. Потому что 2 раза нажать на кнопку - то же самое, что и 0 раз нажать. Составляем линейные уравнения, что сумма нажатий на все кнопки, влияющие на данную лампу - дает 0 или 1 по модулю 2 (в зависимости от того, горит ли эта лампа изначально).

    А дальше эту систему уравнений можно просто решать методом гаусса. Почему? Ведь он работает с вещественными числами? Но нет! Гауссу по-фигу над каким полем работать. Делаем все вычисления по модулю 2 - и получим решение в виде 0 и 1 для всех переменных.

    Поля по модулю простых чисел еще можно, например, использовать для реализации быстрого преобразования фурье для быстрого умножения длинных чисел без использования операций с float, что требуется в стандартной реализации (она вообще с комплексными числами работает). И такая реализация быстрее и точнее.
    Ответ написан
    1 комментарий
  • Как реализовать алгоритм рекурсией?

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

    Ваша ошибка в том, что в слуае "2(с)3(a)" вы попытаетесь 2 раза повторить результат распаковки строки "с)3(a"

    def process(s, begin):
      i = begin;
      ans = ""
      while i < len(s):
        // Внетренность скобок обрабатывается рекурсивно. ) - это конец строки для нас.
        if s[i] == ')': break;
        if '0' <= s[i] and s[i] <= '9':  // если встретили цифру
          k = 0
          // выделяем в k числовое значение записанного числа
          while i < len(s) and '0' <= s[i] and s[i] <= '9':
            // пока встречаются цифры берем их численное значение и приписываем в конец к k
            k = 10*k + ord(s[i]) - ord('0')
            i+=1
          // если после числа идет бува, то просто копируем ее нужное количетсво раз
          if  'a' <= s[i] and s[i] <= 'z': 
            ans += s[i:i+1]*k
            i+=1
          else:
            // иначе - должна быть скобка.
            assert(s[i] == '(')
            // рекурсивно обрабатываем внутренность скобок. Функция вернет нам, где
           // закрывающая скобка для данной.
            (i, cur) = process(s, i+1)
           // копируем результат распаковки строки внутри скобок нужное количетво раз
            ans += cur * k;
        else:
         // цирфы не было. Просто символ без множителя идет в ответ.
          assert('a' <= s[i] and s[i] <= 'z')
          ans += s[i:i+1]
          i += 1
      // мы могли закончить цикл на закрывающей скобке или в конце строки.
      // но скобку надо пропустить. Прибавляем 1 к счетчику.
      if i < len(s): i+=1
      return (i,ans)
    
    
    
    print (process("abc", 0))
    print (process("3a", 0))
    print (process("3(a)", 0))
    print (process("2(ab)", 0))
    print (process("10(a)", 0))
    print (process("2(b2(a))", 0))
    Ответ написан
  • Какой будет ответ?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Если считается, что зависимость выхода от входа линейная, то ее можно составить так:
    y = k*x+b

    И вам даны 2 значения для x и y:
    0 = k*2.3+b
    400 = k*40.6+b


    Вам надо найти k*11.6+b.

    Система из двух линейных уравнений просто решается аналитически, без конкретных значений:
    y1 = k*x1+b
    y2 = k*x2+b
    
    y1-y2 = k*(x1-x2) +(b-b)
    k = (y1-y2)/(x1-x2)
    b = y1 - k*x1 = y1 - (y1-y2)/(x1-x2)*x1
    
    y = (y1-y2)/(x1-x2)*(x-x1) + y1


    Подставляем туда ваши значения при x = :
    y = (0-400)/(2.3-40.6)(11.6-2.3) + 0 = 400/38.3*9.3 = 43.81
    Ответ написан
    Комментировать
  • Почему здесь нельзя решать через жадный алгоритм?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Почему не работает жадность? Вы же сами привели тест, на котором такая жадность не работает.

    Могу еше привести пример. В {1, 2, 3, 2, 1} - ответ 3. Тут надо удалить отрезок 1..5, потом 2..4, потом 1 вхождение элемента 3, Если же форма будет немного другая, например {1, 100, 1000, 100, 1} - то ответ 4. После первого удаления отрезка надо удалять числа по одному.

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

    Это можно доказать так:
    Рассмотрим какое-то решение (последовательность действий). Перенесем все удаления одиночных элементов в самый конец, возможно изменив количество удаляемых за раз элементов. Кроме того, объеденим несколько операций удаления одного и того же элемента в одну, просуммировав количества удаленных элементов. Все шаги-отрезки все так же можно выполнить, потому что мы только перетащили какие-то операции за них, поэтому перед шагом количество элементов могло только увеличится, а значит весь отрезок все так же не содержит нулей. Это решение имеет такое же количество шагов или меньше. Значит можно искать оптимальное решение именно такого вида.


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

    Пока вы так не докажите свой алгоритм, можно смелло считать, что есть еще какой-то тест, где он выдаст не оптимальный ответ.

    Подсказка1: посмотрите на ограничения. n всего лишь 5000. Значит, подразумеватся решение за квадрат. Посмотрите на теги.

    Подсказка2: Можно смотреть на эту задачу так - есть башенки из кубиков высоты a[i]. Можно за раз или покрасить вертикальный отрезок башни у самого верха, или покрасить какой-то горизонтальный отрезок кубиков. Надо покрасить все кубики за наименьшее количетсво шагов. Тут только неочевидно, почему нужно красить горизонтальные отрезки, ведь несколько операций удаления отрезка могут пересекатся. Но тут поможет рассуждение как выше, через изменение любого ответа. Эти операции можно сложить как доски, в виде пирамид, т.ч. верхние операции целиком лежат в нижних операциях. От этого ответ станет не хуже.

    Еще подсказка, подумайте, как можно убрать самый высоко стоящий кубик, какие варианты?
    Ответ написан
    Комментировать
  • Как правильно хранить ключ шифрования для десктопных приложений?

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

    А с поролем - надо какой-то криптографически стойкой kdf-функцией преобразовать его в ключ шифрования и дальше применять хоть AES, хоть какой-то другой алгоритм шифрования.

    Главное самостоятельно ничего криптографического не велосипедить. Берите популярные крипто-библиотеки и используйте стандратные и современные криптографические приметивы.

    Тут, правда, есть проблема - если пользователь мастер пароль забудет - то сохраненные локально данные уже никак не достать. Можно полученный из пароля через KDF ключ как-то простенько зашифровать (или вообще в плейн тексте) и дополнительно выдать пользователю для сохранения на флешке и использовать для восставления пароля: если нет пароля, то ключ из файла применяется, потом данные шифруются с новым ключем, полученным из нового пользовательского пароля. Важно только убедить пользователя не хранить этот файл на том же компьютере. Если видите этот файл в папке с программой, на рабочем столе или в "моих документах" стоит отругать пользователя за пренебрижение к безопасности.

    Для проверки, что пароль правильный, данные надо снабдить какой то контрольной суммой (до шифрования).

    Все остальное - это security through obscurity. Не работает в долгой перспективе.
    Ответ написан
    4 комментария
  • Как объеденить пользователей с общим имейл?

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

    Я там вам ответил, что это класическая задача на нахождение компонет связности в графе. Гуглите DFS, компоненты связности, графы. Я там даже технических деталей кучу привел.
    Ответ написан
  • Как найти в графе все циклы определённой длины?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    В общем случае, чтобы найти все циклы длины n надо подсчитать Tr(A^n)/n. A - матрица смежности. Tr() - это след (сумма элементов на диаганали). Т.е. возводите матрицу смежности из 0 и 1 в степень n и суммируете элементы по диагонали. Делить на n надо, потому что тут циклы считаются ориентированными и упорядочеными. Т.е. A->B->C посдчитается отдельно от B->C->A. Если граф неориентированный, то надо будет дополнительно поделить на 2 в конце.

    Важно, тут будут считаться циклы, ходящие по одним и тем же вершинам и ребрам. Но для n=3 это не важно, если только у вас петель в графе нет.

    Для n=3 можно чуть быстрее - возвести матрицу в квадрат и получить матрицу количеств путей длины 2. Потом можно пройтись по всем ребрам и просуммировать все циклы с этим ребром (известное уже количество путей длины 2 между двумя концами ребра). Тут как бы последнее, третье умножение пропускается и вместо него считаются только элементы на диагонали. Потом надо будет поделить на 3, потому что тут вы считаете каждый цикл 3 раза.

    Можно воспользоваться быстрым умножением матриц Штрессена или присобачить какое-нибудь битовое сжатие, как я уже советовал вам в этом вопросе.

    В зависимости от размерности матрицы (количества вершин) битовое сжатие может быть быстрее Штрассена или какого-либо другого быстрого алгоиртма умножения матриц.
    Ответ написан
  • Алгоритм эффективного размещения?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    *offtopic* Лет 15 назад делал такое для записывания анимешных сериалов на CD-диски, только там было сложнее, потому что сериалы можно разбивать по нескольким дискам и записывать можно толко сериалы целиком. Эх... было время. Сейчас эти 300 дисков даже и прочитать-то нечем. И исходники пропали лет 10 назад =(.

    Как вам уже написали - эта задача о мульти-рюкзаке. Простого и эффективного решения у нее нет.

    Однако, на практике, скорее всего, вам не нужно оптимальное решение - нужна лишь его некоторая аппроксимация. Посмотрите задачу о рюкзаке. Там есть очень простое динамическое программирование с параметрами вида "можно ли используя файлы с 1 по i-ый заполнить ровно k (мега|кило)байт"

    Потом сморите в конце массива для всех файлов - это оптимальные заполнения одной флешки.

    Удалите файлы определенные на эту флешку из рассмотрения и повторяйте процесс.

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

    Потом в переборе пробуйте разные варианты, запускайтесь рекурсивно. Какой-то ответ будет найден моментально. Выходите из перебора, если текущее количество флешек/общее свободное место/сумма квадратов свободных мест превысило оптимальное найденное пока что.

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

    Альтернативно - составьте задачу целочисленного линейного программирование (integer linear programming) и натравите на нее какой-то из солверов. Они сейчас очень продвинутые. Правда тут уж как повезет. Может на вашей задаче вы ответа так и не дождетесь. В качестве переменных берите, что такой-то файл относится к такой-то флешке. Сумма по каждому файлу - ровно один. По каждой флешке сумма размеров файлов * на переменные <= размер флешки. Сумма свободных мест - минимизируется.

    Возможно, можно составить квадратичную целевую функцию, я не знаю, что сейчас солверы умеют. Гуглите quadratic integer programming solver.

    Если хотите минимизировать количество флешек, то можно завести еще переменные - занята ли флешка. Уравнения тут - эта переменная >= всех индикаторынх переменных для всех файлов для этой флешки. Целевая функция - сумма всех переменных занятости флешек.
    Ответ написан
    Комментировать
  • Как эффективно узнать какие вершины соединенные с вершиной A соединены ребром?

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

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

    В таком случае есть более эффективное решение (хоть и с такой же кубической асимптотикой). Суть в том, чтобы перебирать не вершину треугольника, а ребро. Тогда вопрос будет подсчитать, сколько вершин связанны с заданными двумя. Что равносильно: Найти в скольких столбцах матрицы смежности стоят единицы в двух заданных строках. Это линейный проход, но его можно в 64 раза ускорить, если использовать битовое сжатие. Храните матрицу смежности в битах 64-битных чисел. Т.е. один столбец long long матрицы будет отвечать за 64 столбца. В таком виде можно с-AND'ить два числа и потом подсчитать, сколько в числе единичных бит (что тоже можно сделать сильно быстрее, чем за 64 операции).
    Ответ написан
    Комментировать
  • Как исправить программу на pascal, чтобы все тесты прошли?

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

    Заведите 2 массива - min_row и max_column и одним проходом заполните их, используя функции, которые у вас уже есть. И только потом приходитесь по всей матрице и сравнивайте текущий элемент с уже известными максимумом/минимумом.

    В конце выводите тоже через writeln. Инициализируйте максимум/минимум самыми большими возможными значениями или первыми элементами строки/столбца. Что если в матрице все числа 92233720+1?

    P.s В данном контексте строка - row, точка - point. Седловая точка - saddle_point.
    Ответ написан
    1 комментарий
  • Есть задача на JAVA - нет ответа. Как решить задачу?

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

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

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

    Что-то вроде этого (читайте как псевдокод, я джаву не особо знаю):
    public static int isEqual(String firstString, String secondString) {
      int used = 0;
      int[] first = new int[256], second = new int[256];
      if (firstString == secondString) return 1;
      if (firstString.length() != secondString.length()) return 0;
      for (i = 0; i < firstString.lengt(); ++i) {
        int ch1 = Character.getNumericValue(firstString.charAt(i));
        int ch2 = Character.getNumericValue(secondString.charAt(i));
        if (first[ch1] == 0) {
          used++;
        }
        if (first[ch1] != second[ch2]) return 0;
        first[ch1] = i+1;  // +1, потому что изначальные 0 означают "символ не встречался"
        second[ch2] = i+1; 
      }
      if (used == 33) return 0;
      return 1;


    Возможно лучше переписать с Map, я не знаю, что там с кодировками будет, может символы не однобайтовые и 256 в таблицах не хватит.
    Ответ написан
  • Как перемешать массив одинаково для всех?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Вот алгоритм для перемешивания массива, если у вас есть детерминированная функция rand(), которую вы каким-то seed проинициализровали.
    for (i = 1; i<arr.length; ++i) {
     let j = floor(rand()*(i+1));
     let tmp = arr[i];
     arr[i] = arr[j];
     arr[j] = tmp;
    }


    Можно использовать что-то вроде функции, предложенной twobomb, но именно той функцией пользоваться не советую - она выдаст максимум 67 различных вариантов, а на самом деле сильно меньше. Используйте, например, параметры отсюда:
    function rand(){
     	seed = (16,807*seed) % 2,147,483,647;
     	return seed/2,147,483,647;
    }
    Ответ написан
  • Какой выбрать язык для криптографии?

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

    Если же вам для академических целей или интереса ради хочется самим написать какую-то криптографию, то лучше брать язык программирования, где есть удобная длинаая арифметика. Таким является, например, python. Код будет проще и понятнее. В Java можно было бы использовать BigInteger, но им пользоваться неудобно (всякие конструкции вида a.Add(b).Blablabla(c).Blablabla(d)). В C++ можно переопределить операторы для класса длинных чисел, но я не советую писать на C++, если это не ваш любимый уже язык программирования. В питон порог входа пониже.
    Ответ написан