[Esta pregunta es un seguimiento para calcular las ejecuciones de una cadena ]
Un período
p
de una cadenaw
es cualquier número entero positivo dep
modo quew[i]=w[i+p]
siempre que se definan ambos lados de esta ecuación. Dejeper(w)
denotar el tamaño del período más pequeño dew
. Decimos que una cadenaw
es periódica iffper(w) <= |w|/2
.
Entonces, informalmente, una cadena periódica es solo una cadena que se compone de otra cadena repetida al menos una vez. La única complicación es que al final de la cadena no necesitamos una copia completa de la cadena repetida siempre que se repita en su totalidad al menos una vez.
Por ejemplo, considere la cadena x = abcab
. per(abcab) = 3
como x[1] = x[1+3] = a
, x[2]=x[2+3] = b
y no hay un período más pequeño. La cadena, abcab
por lo tanto, no es periódica. Sin embargo, la cadena ababa
es periódica como per(ababa) = 2
.
Como más ejemplos, abcabca
, ababababa
y abcabcabc
también son periódicas.
Para aquellos a quienes les gustan las expresiones regulares, este detecta si una cadena es periódica o no:
\b(\w*)(\w+\1)\2+\b
La tarea es encontrar todas las subcadenas máximas periódicas en una cadena más larga. A veces se les llama carreras en la literatura.
Una subcadena
w
es una subcadena periódica máxima (ejecución) si es periódica y niw[i-1] = w[i-1+p]
tampocow[j+1] = w[j+1-p]
. Informalmente, la "ejecución" no puede estar contenida en una "ejecución" más grande con el mismo período.
Debido a que dos ejecuciones pueden representar la misma cadena de caracteres que ocurren en diferentes lugares en la cadena general, representaremos las ejecuciones por intervalos. Aquí está la definición anterior repetida en términos de intervalos.
Una ejecución (o subcadena periódica máxima) en una cadena
T
es un intervalo[i...j]
conj>=i
tal que
T[i...j]
es una palabra periódica con el puntop = per(T[i...j])
- Es maximo. Formalmente, ni
T[i-1] = T[i-1+p]
tampocoT[j+1] = T[j+1-p]
. Informalmente, la ejecución no puede estar contenida en una ejecución más grande con el mismo período.
Denote por RUNS(T)
el conjunto de ejecuciones en cadena T
.
Ejemplos de carreras
Los cuatro subseries periódicas máximas (carreras) en la cadena
T = atattatt
sonT[4,5] = tt
,T[7,8] = tt
,T[1,4] = atat
,T[2,8] = tattatt
.La cadena
T = aabaabaaaacaacac
contiene los siguientes 7 subcadenas periódicas máximas (carreras):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
.La cadena
T = atatbatatb
contiene las siguientes tres ejecuciones. Ellos son:T[1, 4] = atat
,T[6, 9] = atat
yT[1, 10] = atatbatatb
.
Aquí estoy usando la indexación 1.
La tarea
Escriba el código de manera que para cada número entero n que comience en 2, genere la mayor cantidad de ejecuciones contenidas en cualquier cadena binaria de longitud n
.
Puntuación
Su puntaje es el más alto n
que alcanza en 120 segundos, de modo que, para todos k <= n
, nadie más ha publicado una respuesta correcta más alta que usted. Claramente, si tiene todas las respuestas óptimas, obtendrá la puntuación más alta n
que publique. Sin embargo, incluso si su respuesta no es la óptima, aún puede obtener el puntaje si nadie más puede superarlo.
Idiomas y bibliotecas
Puede usar cualquier idioma y bibliotecas disponibles que desee. Siempre que sea posible, sería bueno poder ejecutar su código, por lo tanto, si es posible, incluya una explicación completa de cómo ejecutar / compilar su código en Linux.
Ejemplo optima
En lo que sigue: 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
¿Qué debería exactamente mostrar mi código?
Para cada uno, n
su código debe generar una sola cadena y el número de ejecuciones que contiene.
Mi máquina Los tiempos se ejecutarán en mi máquina. Esta es una instalación estándar de ubuntu en un procesador AMD FX-8350 de ocho núcleos. Esto también significa que necesito poder ejecutar su código.
Respuestas principales
- 49 por Anders Kaseorg en C . Rosca simple y ejecución con L = 12 (2 GB de RAM).
- 27 por cdlane en C .
fuente
{0,1}
cadenas, indíquelo explícitamente. De lo contrario, el alfabeto podría ser infinito, y no veo por qué sus casos de prueba deberían ser óptimos, porque parece que también buscó{0,1}
cadenas también.n
hasta12
y nunca superó el alfabeto binario. Heurísticamente, esperaría que una cadena binaria sea óptima, porque agregar más caracteres aumenta la longitud mínima de una ejecución.Respuestas:
do
Esto hace una búsqueda recursiva de soluciones óptimas, muy podadas utilizando un límite superior en el número de ejecuciones que podría terminar el resto desconocido de la cadena. El cálculo del límite superior utiliza una tabla de búsqueda gigantesca cuyo tamaño está controlado por la constante
L
(L=11
: 0.5 GiBL=12
,: 2 GiBL=13
,: 8 GiB).En mi computadora portátil, esto sube a través de n = 50 en 100 segundos; la siguiente línea llega a 142 segundos.
Salida:
Aquí están todas las secuencias óptimas para n ≤ 64 (no solo la lexicografía primero), generadas por una versión modificada de este programa y muchas horas de cálculo.
Una notable secuencia casi óptima
Los prefijos de la secuencia fractal infinita
eso es invariante bajo la transformación 101 ↦ 10100, 00 ↦ 101:
parece tener un número de corridas casi óptimo, siempre dentro de 2 del óptimo para n ≤ 64. El número de corridas en los primeros n caracteres dividido por n se aproxima (13 - 5√5) / 2 ≈ 0.90983. Pero resulta que esta no es la proporción óptima; vea los comentarios.
fuente
Como no es una carrera si solo hay un caballo, presento mi solución, aunque es solo una fracción de la velocidad de Anders Kaseorg y solo un tercio como críptico. Compilar con:
El corazón de mi algoritmo es un cambio simple y un esquema XOR:
Una ejecución de ceros en el resultado XOR que sea mayor o igual que el período / cambio actual indica una ejecución en la cadena original para este período. A partir de esto, puede saber cuánto duró la ejecución y dónde comienza y termina. El resto del código está sobrecargado, configurando la situación y decodificando los resultados.
Espero que haga al menos 28 después de dos minutos en la máquina de Lembik. (Escribí una versión de pthread, pero solo logré que funcionara aún más lentamente).
Salida:
fuente
-O3
no está destinado a ser "inseguro". Permite la vectorización automática, pero probablemente no haya nada que vectorizar aquí. A veces puede hacer que el código sea más lento, por ejemplo, si usa un cmov sin ramificaciones para algo donde una ramificación hubiera predicho muy bien. Pero generalmente debería ayudar. Por lo general, también vale la pena probar clang, para ver cuál de gcc o clang hace un mejor código para un bucle específico. Además, casi siempre ayuda usar-march=native
, o al menos-mtune=native
si todavía quieres un binario que se ejecute en cualquier lugar.