Задний план
Инцидент - довольно необычный язык программирования, в котором его список токенов не предопределен, а скорее выведен из входных данных. Таким образом, токенизация программы «Инцидент» может быть довольно сложной, особенно если вы хотите сделать это эффективно. Эта задача о том, чтобы сделать это самостоятельно.
Задание
Ваша программа получит строку в качестве ввода. Вот алгоритм, который использует Incident для токенизации:
- Идентифицируйте все строки, которые встречаются как подстрока ввода, точно тремя способами (то есть есть ровно три вхождения этой строки во входных данных).
- Откажитесь от любой из этих строк, которые являются подстрокой другой такой строки (например, для ввода
ababab
единственной оставшейся строкой будетab
, а неa
илиb
, потому чтоa
иb
обе являются подстрокамиab
). - Откажитесь от любых строк, которые перекрываются внутри ввода. (Например,
aaaa
содержит ровно три копииaa
, но эти копии пересекаются со вторым и третьим символами, поэтому будут отброшены. Аналогичноabababa
, существует три копииab
и три копииba
, но каждый со второго по шестой символы находятся на перекрытия aab
и aba
, поэтому обаab
иba
будут отброшены). - Любые строки, которые остаются в этой точке, являются токенами, используемыми программой. Токенизируйте исходный вход в последовательность этих токенов (из-за сброса на предыдущем шаге будет только один способ сделать это). Любые символы на входе, которые не являются частью какого-либо токена, рассматриваются как комментарии и отбрасываются.
Ваша программа должна принять строку в качестве входных данных и вернуть соответствующий токенизацию строки (список токенов, каждый из которых выражен в виде строк) в качестве выходных данных. Кроме того, это должно быть сделано по крайней мере умеренно эффективно; в частности, программа должна запускаться за квадратичное время («O (n²)») или лучше. (Между прочим, почти наверняка возможно пойти быстрее, чем квадратичный, но это не самый быстрый алгоритм , поэтому не стесняйтесь использовать самый краткий алгоритм, который вы можете найти, который вписывается в пределы сложности.)
Разъяснения
- Хотя программы Incident теоретически могут содержать любой из 256 октетов, для этой задачи для вашей программы приемлемо обрабатывать только входные данные, сформированные из печатного ASCII (включая пробел), плюс перевод строки и табуляцию. (Все известные программы Incident ограничиваются этим подмножеством). Обратите внимание, что пробел / новая строка / табуляция не являются особыми и могут появляться в середине токенов; Инцидент рассматривает все 256 октетов как непрозрачные.
- Определение «квадратичного времени»: «если размер входных данных удвоится, программа будет работать медленнее не более чем на константу плюс коэффициент 4», т. Е. Если t ( x ) - максимальное время, необходимое вашей программе для обработать ввод размера x , тогда должна быть некоторая константа k такая, что t (2 x ) <4 t ( x ) + k для всех x . Имейте в виду, что сравнение строк занимает время, пропорциональное длине строк.
- Ваша программа теоретически должна иметь возможность обрабатывать входные программы любой длины, если они запускаются в (возможно, гипотетическом) варианте вашего языка, который имеет неограниченную память и использует неограниченные целые числа (это нормально, если программа не может достичь этой цели при запуске на практике из-за целые числа или память языка на самом деле очень большие). Вы можете предположить (для целей расчета сложности), что целые числа, которые не превышают длину ввода, можно сравнивать в постоянное время (хотя имейте в виду, что если вы используете большие значения, например, из-за преобразования входных данных в одно целое число, они будут сравниваться пропорционально количеству цифр, которые у них есть).
- Вы можете использовать любой алгоритм, который укладывается в пределы сложности, даже если он не выполняет те же шаги, что и алгоритм, описанный выше, при условии, что он дает те же результаты.
- Эта головоломка о токенизации ввода, а не о форматировании вывода. Если наиболее естественный способ вывода списка на вашем языке связан с неоднозначным форматом (например, с разделением новой строки, когда строки содержат буквальные переводы строки, или без разделителей между строками), не беспокойтесь о том, что вывод заканчивается неоднозначно ( до тех пор, пока список фактически построен). Возможно, вы захотите сделать вторую версию вашего представления, которая дает однозначный вывод, чтобы помочь в тестировании, но исходная версия - это версия, которая имеет значение для оценки.
Прецедент
Для следующей входной строки:
aaabcbcbcdefdfefedghijghighjkllkklmmmmonono-nonppqpq-pqprsrsrstststuvuvu
Ваша программа должна создать следующий список вывода:
a a a bc bc bc d e f d f e f e d gh gh gh k l l k k l pq pq pq u u u
Состояние победы
Это код-гольф , поэтому выигрывает самая короткая действующая (то есть правильная программа ввода-вывода и достаточно быстрая для выполнения) программа, измеренная в байтах.
Ответы:
C (gcc), 324 байта
Функция
f
принимает строку с нулевым символом в конце и выводит токены на стандартный вывод. Все новые строки могут быть удалены из кода ниже.Эта старая 376-байтовая версия немного легче для чтения; объяснение ниже относится к нему.
k(0)
генерирует таблицуt
для шаблонаp
для алгоритма Кнута-Морриса-Пратта.K(c)
обрабатывает следующий символc
строки поиска и обновленийm
, длина наибольшего префиксаp
которых может быть найдена и заканчивается на последнем обработанном символе.В первом
for
цикле для каждого индексаa
в строке мы подсчитываем, сколько раз каждое возможное значениеm
встречается при поиске во всей строке подстроки, начинающейся сa
. Затем мы ищем наибольшееl
такое, чтобы длинаl
подстроки начиналасьa
ровно 3 раза. Если он достаточно короткий, чтобы полностью содержаться в строке, найденной для предыдущегоa
, мы игнорируем ее. Если оно перекрывается, мы удаляем предыдущую строку изz
массива, запись которого токены будут сохранены. В противном случае его длина сохраняется вz
.Затем мы снова используем KMP для поиска в строке токенов, записанных в
z
. Если один из них найден в местоположении с записью 0z
, мы знаем, что этот токен был удален из-за перекрытия. Если токен не был удален, он печатается.источник
O(n^2)
или быстрее. И почему там!!
на!!z[x-m]
?*Z
это длина следующего токена, который должен стать 0, если любое из других вхождений токена имеет значение 0 в своем индексе в массиве, или оставить то же значение иначе (в этом случае!!z[x-m]
должно быть 1.!!
это так.!!x
должно бытьx
, или это вызывает трюк, о котором я не знаю?!!x
делаетx
логическое значение, представляющее его "правдивость". Итак,!!1 == true
и!!0 == false
. Я не знаю C конкретно, но так обычно и бываетJavaScript,
878867842825775752717712704673664650641 байтСпасибо @Kritixi Lithos за помощь в коде игры в гольф.
Спасибо @ User2428118 за то, что вы играли в гольф 14 байтов.
(Не будет работать в IE7) (Новые строки должны быть введены как «
\n
» и табуляция как «\t
» во входной строке, любые символы Unicode должны быть введены как\u####
)Попробуйте онлайн
Объяснение того, как это работает, и разглаженный код
Во-первых, программа генерирует массивы Кнута Морриса Пратта для каждой возможной подстроки;
Затем программа находит максимальную длину совпадения для каждого индекса в слове с каждой подстрокой. (это время O (n ^ 2))
Программа использует эти данные, чтобы найти самые длинные подстроки, которые появляются три раза для каждого начального символа в строке.
Программа использует эти данные для устранения всех подстрок, которые не появляются ровно три раза, и всех подстрок самой длинной допустимой подстроки.
Затем программа устанавливает все перекрывающиеся или частичные подстроки как подлежащие удалению.
Для каждого из подлежащих удалению значений также удаляются все эквивалентные подстроки.
Наконец, программа объединяет массив подстрок и выводит его.
источник
while
иif
блоки , которые имеют только 1 заявление внутри них. Вы можете удалить скобки{}
вокруг этого утверждения. Например,if(word.charAt(index+i)==word.charAt(index+j)){j++;}
может статьif(word.charAt(index+i)==word.charAt(index+j))j++;
&&
s для заменыif
операторов, я перемещал операторы в циклах, чтобы они заканчивались одним оператором, чтобы я мог удалить фигурные скобки. Я использовал троичные, чтобы заменить некоторые операторы if. Я переместил вещи вокруг и закончил в 946 байтах . Если вы не понимаете, что я сделал, не стесняйтесь спрашивать меня :)