[Этот вопрос является продолжением для вычисления прогонов строки ]
Период
p
строкиw
- это любое положительное целое число,p
такое, чтоw[i]=w[i+p]
когда бы ни были определены обе стороны этого уравнения. Позвольтеper(w)
обозначить размер наименьшего периодаw
. Мы говорим, что строкаw
периодическая тогда и только тогдаper(w) <= |w|/2
.
Так что неофициально периодическая строка - это просто строка, которая состоит из другой строки, повторенной хотя бы один раз. Единственное осложнение состоит в том, что в конце строки нам не требуется полная копия повторяемой строки, если она повторяется полностью хотя бы один раз.
Для примера рассмотрим строку x = abcab
. per(abcab) = 3
как x[1] = x[1+3] = a
, x[2]=x[2+3] = b
и нет меньшего периода. Поэтому строка не abcab
является периодической. Тем не менее, строка ababa
является периодической как per(ababa) = 2
.
Чем больше примеров, abcabca
, ababababa
и abcabcabc
также являются периодическими.
Для тех, кто любит регулярные выражения, этот определяет, является ли строка периодической или нет:
\b(\w*)(\w+\1)\2+\b
Задача состоит в том, чтобы найти все максимальные периодические подстроки в более длинной строке. Это иногда называют пробегами в литературе.
Подстрока
w
- это максимальная периодическая подстрока (прогон), если она периодическая, и ни,w[i-1] = w[i-1+p]
ниw[j+1] = w[j+1-p]
. Неформально «прогон» не может содержаться в более крупном «прогоне» с тем же периодом.
Поскольку два прогона могут представлять одну и ту же строку символов, встречающихся в разных местах общей строки, мы будем представлять прогоны по интервалам. Здесь приведенное выше определение повторяется в терминах интервалов.
В рабочем цикле (или максимальная периодическая подстроки) в строке
T
представляет собой интервал[i...j]
сj>=i
, таким образом, что
T[i...j]
является периодическим словом с точкойp = per(T[i...j])
- Это максимально. Формально, ни,
T[i-1] = T[i-1+p]
ниT[j+1] = T[j+1-p]
. Неофициально прогон не может содержаться в большем прогоне с тем же периодом.
Обозначим через RUNS(T)
множество прогонов в строке T
.
Примеры прогонов
Четыре максимальные периодические подстроки (бежит) в строке
T = atattatt
являютсяT[4,5] = tt
,T[7,8] = tt
,T[1,4] = atat
,T[2,8] = tattatt
.Строка
T = aabaabaaaacaacac
содержит следующие 7 максимальных периодических подстроки (запускается):T[1,2] = aa
,T[4,5] = aa
,T[7,10] = aaaa
,T[12,13] = aa
,T[13,16] = acac
,T[1,8] = aabaabaa
,T[9,15] = aacaaca
.Строка
T = atatbatatb
содержит следующие три прогона. Они являютсяT[1, 4] = atat
,T[6, 9] = atat
иT[1, 10] = atatbatatb
.
Здесь я использую 1-индексирование.
Задание
Напишите код, чтобы для каждого целого числа n, начиная с 2, вы выводили наибольшее количество прогонов, содержащихся в любой двоичной строке длины n
.
Гол
Ваш результат является самым высоким, которого n
вы достигли за 120 секунд, так что k <= n
ни для кого больше никто не опубликовал более правильный ответ, чем вы. Очевидно, что если у вас есть все оптимальные ответы, вы получите балл за самый высокий n
пост. Тем не менее, даже если ваш ответ не является оптимальным, вы все равно можете получить счет, если никто другой не сможет его побить.
Языки и библиотеки
Вы можете использовать любой доступный язык и библиотеки, которые вам нравятся. Там, где это возможно, было бы хорошо иметь возможность запускать ваш код, поэтому, пожалуйста, включите полное объяснение того, как запускать / компилировать ваш код в Linux, если это вообще возможно.
Пример оптима
В следующем: n, optimum number of runs, example string
.
2 1 00
3 1 000
4 2 0011
5 2 00011
6 3 001001
7 4 0010011
8 5 00110011
9 5 000110011
10 6 0010011001
11 7 00100110011
12 8 001001100100
13 8 0001001100100
14 10 00100110010011
15 10 000100110010011
16 11 0010011001001100
17 12 00100101101001011
18 13 001001100100110011
19 14 0010011001001100100
20 15 00101001011010010100
21 15 000101001011010010100
22 16 0010010100101101001011
Что именно должен выводить мой код?
Для каждого n
вашего кода необходимо вывести одну строку и количество прогонов, которые он содержит.
Моя машина Время будет запущено на моей машине. Это стандартная установка Ubuntu на восьмиъядерный процессор AMD FX-8350. Это также означает, что мне нужно иметь возможность запускать ваш код.
Ведущие ответы
- 49 Андерс Kaseorg в C . Однопоточный и работает с L = 12 (2 ГБ ОЗУ).
- 27 по cdlane в C .
источник
{0,1}
-строки, укажите это явно. В противном случае алфавит мог бы быть бесконечным, и я не понимаю, почему ваши тесты должны быть оптимальными, потому что кажется, что вы искали только{0,1}
строки.n
до,12
и он никогда не побивал двоичный алфавит. Эвристически я ожидал бы, что двоичная строка должна быть оптимальной, потому что добавление большего количества символов увеличивает минимальную длину цикла.Ответы:
С
Это делает рекурсивный поиск оптимальных решений, сильно сокращенных с использованием верхней границы числа прогонов, которые могут быть завершены неизвестным остатком строки. При вычислении верхней границы используется гигантская справочная таблица, размер которой контролируется константой
L
(L=11
: 0,5 ГиБ,:L=12
2 ГиБ,:L=13
8 ГиБ).На моем ноутбуке это увеличивается до n = 50 за 100 секунд; следующая строка идет через 142 секунды.
Выход:
Вот все оптимальные последовательности для n ≤ 64 (не только сначала лексикографически), сгенерированные модифицированной версией этой программы и многими часами вычислений.
Замечательная почти оптимальная последовательность
Префиксы бесконечной фрактальной последовательности
которая инвариантна относительно преобразования 101 ↦ 10100, 00 ↦ 101:
Похоже, что число прогонов почти оптимально - всегда в пределах 2 от оптимального для n ≤ 64. Количество прогонов в первых n символах, разделенное на n подходов (13 - 5√5) / 2 ≈ 0,90983. Но оказывается, что это не оптимальное соотношение - см. Комментарии.
источник
Поскольку это не гонка, если есть только одна лошадь, я отправляю свое решение, хотя это лишь небольшая часть скорости Андерса Касерга и всего лишь третья часть, как загадочная. Компилировать с:
Сердцем моего алгоритма является простая схема shift и XOR:
Прогон нулей в результате XOR, который больше или равен текущему периоду / смещению, указывает прогон в исходной строке за этот период. Из этого вы можете сказать, как долго длился бег, где он начинается и заканчивается. Остальная часть кода является служебной, настраивая ситуацию и декодируя результаты.
Я надеюсь, что это сделает по крайней мере 28 после двух минут на машине Лембика. (Я написал pthread-версию, но только смог заставить ее работать еще медленнее.)
Выход:
источник
-O3
не предназначен быть «небезопасным». Это позволяет автоматически векторизовать, но, вероятно, здесь нечего векторизовать. Иногда он может замедлить выполнение кода, например, если он использует cmov без веток для чего-то, что ветвление предсказывало бы очень хорошо. Но обычно это должно помочь. Также стоит попробовать clang, чтобы увидеть, какой из gcc или clang делает лучший код для конкретного цикла. Кроме того, это почти всегда помогает использовать-march=native
, или, по крайней мере,-mtune=native
если вы все еще хотите двоичный файл, который работает где угодно.