Задание
- Вам дана изменяемая строка, которая соответствует
[a-z]+( [a-z]+)*
. - Вы должны преобразовать его в строку, содержащую те же слова, но в обратном порядке, чтобы «всем привет всем» стало «всем привет».
- Вам не разрешается использовать больше, чем постоянный объем дополнительной памяти (поэтому не следует копировать всю строку или любое целое слово в только что выделенный буфер).
- Там нет ограничений по времени. Быть безнадежно неэффективным не повредит вашему счету.
- Если выбранный вами язык не допускает мутации строк, массивы символов являются приемлемой заменой.
Твой счет
- Ваша оценка рассчитывается исключительно по количеству назначений, которые вы делаете для строковых элементов (маленькие оценки лучше всего). Если вы используете библиотечную функцию, которая записывает в строку, ее число также учитывается.
- Предположим, количество назначений, которые вам нужны для ввода s, равно n (s) . Тогда ваш результат является максимальным (педантично, супремум) по всем входам s (соответствует регулярному выражению, указанному выше), равному n (s) / length (s) . Если вы не можете рассчитать это точно, вы можете использовать нижнюю верхнюю границу, которую вы можете доказать.
- Вы можете разорвать ничью, если докажете, что ваш алгоритм использует асимптотически меньшее количество назначений (это может произойти, даже если у вас одинаковый счет, см. Ниже). Если вы не можете сделать это, вы можете разорвать связь, показав, что вы используете меньше дополнительной памяти. Однако первое условие разрыва связи всегда имеет приоритет.
- Для некоторых входных данных каждый символ должен измениться, поэтому невозможно набрать меньше 1.
- Я могу придумать простой алгоритм со счетом 2 (но я не вхожу в него).
Заметки о супреме и связях
- Супремум набора чисел - это наименьшее число, которое не меньше любого из них. Это очень похоже на максимум набора, за исключением того, что некоторые бесконечные наборы, такие как {2/3, 3/4, 4/5, 5/6, ...}, не имеют единственного элемента максимума, но все равно будут иметь супремум, в этом случае 1.
- Если вам удастся «сохранить» только постоянное количество назначений по моему алгоритму оценки 2 (скажем), ваша оценка все равно будет 2, потому что вы будете сколь угодно приближаться к 2, чем больше будет ваш ввод. Тем не менее, вы выиграете на тай-брейке, если до этого дойдет.
code-challenge
Бен Милвуд
источник
источник
little-omega(1)
пространство, помещая его в стек.O(1)
дополнительным пространством невозможно . Вам нужноO(log n)
место для хранения позиции индекса, поскольку k-битное целое число может хранить в них только строки длиной до2^k
. Ограничение длины строк делает задачу довольно бессмысленной, так как каждый алгоритм требуетO(1)
места таким образом.Ответы:
Python, оценка:
21,51,25Это прямой комбинация ответа Примо и моего ответа. Так что кредиты ему тоже!
Доказательство еще в процессе, но вот код для игры! Если вы можете найти встречный пример оценки выше 1,25 (или если есть ошибка), дайте мне знать!
В настоящее время худший случай:
где каждая буква «a», «b», «c» и «» (пробел) содержит ровно n букв, и ровно две буквы «d». Длина строки - 4n + 2, а количество назначений - 5n + 2 , что дает оценку 5/4 = 1,25. .
Алгоритм работает в два этапа:
k
такое чтоstring[k]
иstring[n-1-k]
являются границами словаstring[:k]+string[n-1-k:]
(т.е. объединение первогоk
и последнегоk
символов) с небольшой модификацией.где
n
длина строки.Улучшение, которое дает этот алгоритм, происходит от «малой модификации» на шаге 2. По сути, это знание того, что в объединенной строке символы в позиции
k
иk+1
границы слова (что означает, что они являются пробелами или первым / последним символом в слове), и поэтому мы можем напрямую заменить символы в позицииk
иk+1
соответствующими символами в последней строке, сохранив несколько назначений.Это удаляет наихудший случай из алгоритма обращения слов хостаСуществуют случаи, когда мы не можем на самом деле найти такое
k
, в этом случае мы просто запускаем «алгоритм обращения любого слова» для всей строки.Код длинен, чтобы обрабатывать эти четыре случая при запуске алгоритма обращения слов в «объединенной» строке:
k
не найден (f_long = -2
)string[k] != ' ' and string[n-1-k] != ' '
(f_long = 0
)string[k] != ' ' and string[n-1-k] == ' '
(f_long = 1
)string[k] == ' ' and string[n-1-k] != ' '
(f_long = -1
)Я уверен, что код может быть сокращен. В настоящее время это долго, потому что у меня не было четкой картины всего алгоритма в начале. Я уверен, что можно разработать его так, чтобы он был представлен более коротким кодом =)
Примерный прогон (первый мой, второй - primo):
Вы можете видеть, что оценка почти одинакова, за исключением наихудшего случая алгоритма обращения слов в третьем примере, для которого мой подход дает оценку ниже 1,25.
Python, оценка: 1,5
Точное количество назначений можно аппроксимировать по формуле:
в худшем случае:
с 55 назначениями на строку длиной 37.
Идея похожа на мою предыдущую, просто в этой версии я попытался найти префикс и суффикс на границах слов с разницей в длине не более 1. Затем я запустил свой предыдущий алгоритм с этим префиксом и суффиксом (представьте, что они объединены) , Затем перейдите к необработанной части.
Например, для предыдущего наихудшего случая:
сначала мы сделаем обращение слов «ab» и «c» (4 назначения):
Мы знаем, что на границе это было пространство (есть много случаев, которые нужно обрабатывать, но вы можете это сделать), поэтому нам не нужно кодировать пространство на границе, это главное улучшение по сравнению с предыдущим алгоритмом. ,
Затем, наконец, мы бежим по четырем средним символам, чтобы получить:
всего 8 назначений, оптимальных для этого случая, поскольку все 8 символов изменены.
Это устраняет наихудший случай в предыдущем алгоритме, поскольку наихудший случай в предыдущем алгоритме исключается.
Посмотрите пример прогона (и сравнение с ответом @ primo - его вторая строка):
Ответ primo, как правило, лучше, хотя в некоторых случаях я могу иметь преимущество в 1 балл =)
Также его код намного короче моего, хаха.
Python, оценка: асимптотически 2, в обычном случае гораздо меньше
старый код удален из-за нехватки места
Идея заключается в том , чтобы перебирать каждый индекс, и для каждого индекса
i
, мы берем характер, вычислить новое положениеj
, запомните символ в позицииj
, присвоить символ ,i
чтобыj
, и повторите с символом с индексомj
. Поскольку для вычисления новой позиции нам нужна информация о пробелах, я кодирую старый пробел как заглавную версию новой буквы, а новый пробел - как @.источник
length(string)/3
заставить каждое слово в худшем случае как минимум иметь длину 2), тогда оценка будет меньше 2 (в приведенном выше примере это будет 1,67)if any(process_loop(...) for j in range(...))
не нужно ли подсчитывать назначения из этих циклов процесса?dry_run
параметр имеет ненулевое значение (значениеi
). Внутриprocess_loop
, еслиdry_run
он не равен нулю, он не будет выполнять никаких заданий.Perl, оценка 1.3̅
Для каждого непробельного символа выполняется одно назначение, а для каждого пробельного символа два назначения.
Поскольку пробельные символы не могут превышать половины общего числа символов, наихудший результат равен 1,5 .Алгоритм не изменился, но я могу доказать нижнюю верхнюю границу. Давайте сделаем два замечания:
Затем можно увидеть, что теоретический «наихудший случай» с асимптотически 1/2 пробелом не является наихудшим случаем вообще:
ab c d e f g h i ...
На самом деле, это довольно хороший случай.
Чтобы не допустить наблюдения одного и двух выше, каждое односимвольное слово должно быть перемещено в середину слова длиной три или более символов. Это предполагает наихудший случай, содержащий асимптотически 1/3 пробела:
a bcd a bcd a ... bc
Или, что то же самое, только двухсимвольные слова:
a bc de fg hi jk ...
Поскольку наихудший случай содержит асимптотически 1/3 пробела, наихудший случай становится 1,3̅ .
Изменить: Добавлен счетчик свопов и соотношение.
Ввод взят из стандартного ввода. Пример использования:
метод
Для начала первый символ строки перемещается в конечный пункт назначения. Только что замененный персонаж перемещается к месту назначения и т. Д. Это продолжается до тех пор, пока не будет выполнено одно из двух условий:
Когда это происходит, персонаж не заменяется пробелом, а скорее на зеркальную позицию пробела. Алгоритм продолжается с этой позиции.
Когда цель возвращается в начальную начальную позицию текущего цикла, необходимо найти следующий символ без отмены (или, скорее, любой символ без отмены). Чтобы сделать это при постоянных ограничениях памяти, все перестановки, сделанные до этого момента, отслеживаются в обратном направлении.
После этой фазы каждый непробельный символ перемещается не более одного раза. Чтобы закончить, все пробелы поменялись местами с символами в их положениях зеркала, требуя две операции присваивания за пробел.
источник
Рубин, оценка 2
Как стартер очень простой алгоритм. Сначала он переворачивает всю строку, а затем снова переворачивает каждое слово в строке. В худшем случае (одно слово, четное количество символов) счет становится равным 2.
Использование:
источник
C ++: оценка 2
источник
Rebol
Мне неясно на счет для этого. В этом коде нет прямого назначения строки. Все обрабатывается тем,
reverse/part
который выполняет реверсирование внутри и изначально на всей цепочке.Некоторые подробности о коде:
Цикл по строке (
series
), пока не достигнетtail?
Первый раз в цикле сделать полный оборот строки -
reverse/part series tail series
(что так же, какreverse series
)Затем поменяйте местами каждое найденное слово на следующих итерациях -
reverse/part series find series space
Когда найденное слово будет найдено, вернитесь
tail series
назад, чтобы оно перевернуло последнее слово в строке -reverse/part series tail series
Rebol позволяет обойти строку через внутренний указатель . Вы можете увидеть это в
series: next f
(переместить указатель на пробел, чтобы начало следующего слова) иseries: head series
(сбросить указатель на голову).Смотрите серию для получения дополнительной информации.
Пример использования в консоли Rebol:
источник
C: оценка 2
Это просто переворачивает всю строку один раз, а затем переворачивает каждое слово.
источник
Питон: оценка 2
Почти идентичен алгоритму Говарда, но делает шаги обращения в обратном порядке (сначала переворачивает слова, затем переворачивает всю строку). Дополнительные б памяти 3 переменные байты размера:
i
,j
иt
. Технически,find
иlen
используют некоторые внутренние переменные, но они могут также легко использоваться повторноi
илиj
без потери функции.быстрое редактирование: сохранение строковых назначений только путем замены, когда символы различаются, поэтому я могу получить некоторые дополнительные точки из заметки № 2.
источник
партия
Я признаю, что не полностью понимаю оценку (я думаю, что это два) .., но я скажу - это делает вещь .
Ввод принимается как первое стандартное входное значение и поэтому должен быть заключен в кавычки -
вызов:
script.bat "hello there everyone"
из:
everyone there hello
.Может быть, кто-то другой может забить меня (при условии, что я не дисквалифицировал себя каким-либо другим способом).
источник
Javascript
Использование:
У меня странное чувство, что я что-то пропустил ...
источник