Regex (ECMAScript 2018 o .NET), 140 126 118 100 98 82 bytes
^(?!(^.*)(.+)(.*$)(?<!^\2|^\1(?=(|(<?(|(?!\8).)*(\8|\3$){1}){2})*$).*(.)+\3$)!?=*)
Esto es mucho más lento que la versión de 98 bytes, porque ^\1queda de la búsqueda anticipada y, por lo tanto, se evalúa después. Vea a continuación un cambio simple que recupera la velocidad. Pero debido a esto, los dos TIO a continuación se limitan a completar un conjunto de casos de prueba más pequeño que antes, y el .NET es demasiado lento para verificar su propia expresión regular.
Pruébalo en línea! (ECMAScript 2018) ¡
Pruébelo en línea! (.RED)
Para eliminar 18 bytes (118 → 100), robé descaradamente una optimización realmente agradable de la expresión regular de Neil que evita la necesidad de poner una mirada hacia adelante dentro de la mirada negativa hacia atrás (produciendo una expresión regular sin restricciones de 80 bytes). Gracias Neil!
¡Eso se volvió obsoleto cuando dejó caer unos increíbles 16 bytes más (98 → 82) gracias a las ideas de jaytea que llevaron a una expresión regular sin restricciones de 69 bytes! Es mucho más lento, ¡pero eso es golf!
Tenga en cuenta que los (|(no-ops para hacer que la expresión regular esté bien vinculada tienen el resultado de hacer que se evalúe muy lentamente en .NET. No tienen este efecto en ECMAScript porque las coincidencias opcionales de ancho cero se tratan como no coincidencias .
ECMAScript prohíbe los cuantificadores de afirmaciones, por lo que esto hace que el golf sea más difícil para los requisitos de fuentes restringidas . Sin embargo, en este punto está tan bien jugado que no creo que levantar esa restricción en particular abriría más posibilidades de golf.
Sin los caracteres adicionales necesarios para que pase las restricciones ( 101 69 bytes):
^(?!(.*)(.+)(.*$)(?<!^\2|^\1(?=((((?!\8).)*(\8|\3$)){2})*$).*(.)+\3))
Es lento, pero esta edición simple (por solo 2 bytes adicionales) recupera toda la velocidad perdida y más:
^(?!(.*)(.+)(.*$)(?<!^\2|(?=\1((((?!\8).)*(\8|\3$)){2})*$)^\1.*(.)+\3))
^
(?!
(.*) # cycle through all starting points of substrings;
# \1 = part to exclude from the start
(.+) # cycle through all ending points of non-empty substrings;
# \2 = the substring
(.*$) # \3 = part to exclude from the end
(?<! # Assert that every character in the substring appears a total
# even number of times.
^\2 # Assert that our substring is not the whole string. We don't
# need a $ anchor because we were already at the end before
# entering this lookbehind.
| # Note that the following steps are evaluated right to left,
# so please read them from bottom to top.
^\1 # Do not look further left than the start of our substring.
(?=
# Assert that the number of times the character \8 appears in our
# substring is odd.
(
(
((?!\8).)*
(\8|\3$) # This is the best part. Until the very last iteration
# of the loop outside the {2} loop, this alternation
# can only match \8, and once it reaches the end of the
# substring, it can match \3$ only once. This guarantees
# that it will match \8 an odd number of times, in matched
# pairs until finding one more at the end of the substring,
# which is paired with the \3$ instead of another \8.
){2}
)*$
)
.*(.)+ # \8 = cycle through all characters in this substring
# Assert (within this context) that at least one character appears an odd
# number of times within our substring. (Outside this negative lookbehind,
# that is equivalent to asserting that no character appears an odd number
# of times in our substring.)
\3 # Skip to our substring (do not look further right than its end)
)
)
Lo escribí usando lookahead molecular ( 103 69 bytes) antes de convertirlo en lookbehind de longitud variable:
^(?!.*(?*(.+)(.*$))(?!^\1$|(?*(.)+.*\2$)((((?!\3).)*(\3|\2$)){2})*$))
^
(?!
.*(?*(.+)(.*$)) # cycle through all non-empty substrings;
# \1 = the current substring;
# \2 = the part to exclude from the end
(?! # Assert that no character in the substring appears a
# total even number of times.
^\1$ # Assert that our substring is not the whole string
# (i.e. it's a strict substring)
|
(?*(.)+.*\2$) # \3 = Cycle through all characters that appear in this
# substring.
# Assert (within this context) that this character appears an odd number
# of times within our substring.
(
(
((?!\3).)*
(\3|\2$)
){2}
)*$
)
)
Y para ayudar a que mi expresión regular esté bien vinculada, he estado usando una variación de la expresión regular anterior:
(?*(.+)(.*$))(?!^\1$|(?*(.)+.*\2$)((((?!\3).)*(\3|\2$)){2})*$)\1
Cuando se usa con regex -xml,rs -o, esto identifica una subcadena estricta de la entrada que contiene un número par de cada carácter (si existe). Claro, podría haber escrito un programa sin expresiones regulares para hacer esto por mí, pero ¿dónde sería la diversión en eso?
abcbca -> False.