Regex (Perl / PCRE2 / Boost / Pythonregex
), 59 47 46 38 37 33 32 bytes
^((.+)((.)(?3)\4|(?=\2$).?))?.?$
Try it online! - Perl v5.28.2 / Attempt This Online! - Perl v5.36+
Try it online! - PCRE2 v10.33 / Attempt This Online! - PCRE2 v10.40+
Try it online! - Boost
Attempt This Online! - Python import regex
This has been a special-case rollercoaster. The original versions needed |^.$
to match one-character strings – and then two 38 byte versions suddenly didn't need that anymore, and just naturally matched one-character strings. And then at 37 the |^.$
was back... it stuck around during the drop to 33 bytes.
Now at 32 bytes, it's still there, but hiding. It's subsumed into the rest of the expression, at the cost of also matching an empty string (which is allowed by the challenge's rules).
The following explanation is for the 33 byte version, because it's easier to format the comments. The transformation is ^pattern.?$|^.$
→ ^(pattern)?.?$
, with all of pattern
's capture group indices incremented by 1.
^ # Anchor start
(.+) # Match the longest prefix that we conjecture (but later prove)
# recurs as a suffix, and capture it in \1.
# Match the palindromic subsection at the end, using recursion
( # Define recursive subroutine (?2)
(.) # \3 = one character from the left side
(?2) # Call self recursively
\3 # Match the same character on the right side
| # or...
(?=\1$) # End the recursion. Assert that the remaining suffix exactly
# matches the prefix we captured.
.? # Optionally skip one character, which will happen if the
# palindromic portion is odd in length.
)
.? # Optionally skip one character at the end. This will have been
# verified already by the "(?=\1$)" above, so we don't need to
# do so again.
$ # Assert we've reached the end of the string.
|^.$ # Match a one-character string as a special case, since the
# above algorithm only works on multi-character tralindromes.
A previous 38 byte version was PCRE2-only due to use of non-atomic lookahead, but that has turned out to be unnecessary.
Regex (Ruby), 53 52 44 43 39 38 bytes
^((.+)((.)\g<3>\k<4+0>|(?=\2$).?))?.?$
Try it online!
This is the 47 46 38 37 33 32 byte regex ported to Ruby's subroutine call syntax. \k<4+0>
retrieves \4
from the current level of recursion, whereas what \4
would retrieve may have been overwritten at a deeper level of recursion.
Regex (.NET), 35 bytes
^(.?(.)*.?)(?<-2>\2)*(?(2)^)\1$|^.$
Try it online!
From Neil's Retina answer; please see his post for the explanation. Beats my .NET regex by 20 12 11 7 6 bytes.
I have ported it to my style here, optimizing for the best speed at its length. Moving the special-case test to the end should make it theoretically faster to match any valid multi-character tralindrome, at the cost of being slower to match a one-character string.
Ported to PCRE2, this is 55 bytes, demonstrating that what's optimal in one regex engine isn't necessarily so in others:
^(?*((.?).*(?=(.*))(.?)))\2((.)(?5)\6|(?=\3$)\4)\1$|^.$
Attempt This Online! - PCRE2 v10.40+
Regex (.NET), 64 63 55 47 46 42 41 bytes
^((.+)(.)*(?=\2$).?(?<-3>\3)*(?(3)^))?.?$
Try it online!
At 55 47 46 42 41 bytes, this is now a port of the 38 37 33 32 byte Perl/PCRE2/Boost regex.
((.)(?3)\4|pattern3.?)
becomes (.)*pattern3.?(?<-3>\3)*(?(3)^)
, using .NET's balancing groups instead of recursion to match the palindromic subsection:
(.)*
- capture the left half of the palindrome, pushing it onto the \3
stack one character at a time.
.?
- optionally skip one character, in the case that the palindrome is odd in length.
(?<-3>\3)*
- pop every character off the \3
stack, matching them in reverse order.
(?(3)^)
- assert that if the \3
stack isn't empty, then something impossible (that we're at the start of the string) is true – i.e. assert that the \3
stack is empty.
Regex (Perl / PCRE / .NET), 61 57 51 bytes
^(.+)((.?)(?=.*?(\3(?(4)\4|.?)$)))+(?=\1$).?\4$|^.$
Try it online! - Perl v5.28.2 / Attempt This Online! - Perl v5.36+
Try it online! - PCRE1
Try it online! - PCRE2 v10.33 / Attempt This Online! - PCRE2 v10.40+
Try it online! - .NET
This is a port of the 46 38 33 byte Perl/PCRE2/Boost version, to regex engines that don't have recursion (or do have it, but not in a non-atomic form, in the case of PCRE1). To match the palindromic subsection, group-building (with a nested backreference) is used instead of recursion.
Regex (Java / Perl / PCRE / .NET), 66 56 bytes
^(.+?)((.?)(?=.*?(\3(\4|(?!\6).?)$))())+(?=\1$).?\4$|^.$
Try it online! - Java 12.0.2 / Attempt This Online! - Java 18.0.1+
Try it online! - Perl v5.28.2 / Attempt This Online! - Perl v5.36+
Try it online! - PCRE1
Try it online! - PCRE2 v10.33 / Attempt This Online! - PCRE2 v10.40+
Try it online! - .NET
This is a port of the 61 51 byte Perl/PCRE/.NET version, to regex engines that don't have conditionals but do have nested backreferences.
Java seems to have some bugs in its regex engine, especially in the vein of giving up on backtracking when there are too many possibilities (and there don't even have to be all that many). To work around this, (.+?)
is used instead of (.+)
. If Java's regex engine were really behaving well, this wouldn't be necessary.
Some other variants don't play nice in Java. For example, a port of the previous 57 byte Perl/PCRE/.NET version is 62 bytes:
^(.*)(?=(.)).?((.)(?=.*(\4(\5()|(?!\7))$)))*(?=\1\2$).?\5$|^.$
But fails in Java: Try it online! (12.0.2) / Attempt This Online! (18.0.1+)
This warrants further research.
Regex (Perl / PCRE / Pythonregex
/ .NET), 61 59 bytes
^(.+)((.?)(?=.*?(?=(\3(?(5)\5|.?)$))(\4)))+(?=\1$).?\4$|^.$
Try it online! - Perl v5.28.2 / Attempt This Online! - Perl v5.36+
Try it online! - PCRE1
Try it online! - PCRE2 v10.33 / Attempt This Online! - PCRE2 v10.40+
Attempt This Online! - Python import regex
Try it online! - .NET
This is a port of the 51 byte Perl/PCRE/.NET regex to engines that lack nested backreferences. To emulate them, \4
and \5
are copied back and forth to each other.
Ruby does not work with this regex at all. It just hangs.
Regex (Java / Perl / PCRE / Pythonregex
/ .NET), 64 bytes
^(.+?)((.?)(?=.*?(?=(\3(\6|(?!\7).?)$))(\4))())+(?=\1$).?\4$|^.$
Try it online! - Java 12.0.2 / Attempt This Online! - Java 18.0.1+
Try it online! - Perl v5.28.2 / Attempt This Online! - Perl v5.36+
Try it online! - PCRE1
Try it online! - PCRE2 v10.33 / Attempt This Online! - PCRE2 v10.40+
Try it online! - Python import regex
/ Attempt This Online!
Try it online! - .NET
This combines both the 56 Java and 59 byte Python ports, to support five different regex engines.
\$\large\textit{Anonymous functions}\$
Perl, 49 45 44 bytes
sub{pop=~/^((.+)((.)(?3)\4|(?=\2$).?))?.?$/}
Try it online!
Ruby, 53 49 48 bytes
->s{s=~/^((.+)((.)\g<3>\k<4+0>|(?=\2$).?))?.?$/}
Try it online!
$args-match'^(.?(.)*.?)(?<-2>\2)*(?(2)^)\1$|^.$'
Try it online!
Julia v1.2+, 54 50 49 bytes
s->endswith(s,r"^((.+)((.)(?3)\4|(?=\2$).?))?.?")
Attempt This Online!
R, 58 53 52 bytes
\(L)grepl('^((.+)((.)(?3)\\4|(?=\\2$).?))?.?$',L,,1)
Attempt This Online!
PHP, 64 60 59 bytes
fn($s)=>preg_match('/^((.+)((.)(?3)\4|(?=\2$).?))?.?$/',$s)
Try it online!
lambda s:regex.match(r'((.+)((.)(?3)\4|(?=\2$).?))?.?$',s);import regex
Attempt This Online!
lambda s:__import__('regex').match(r'((.+)((.)(?3)\4|(?=\2$).?))?.?$',s)
Attempt This Online!
(If it must be a pure lambda.)
Java, 85 75 bytes
s->s.matches("((.+?)((.?)(?=.*?(\\4(\\5|(?!\\7).?)$))())+(?=\\2$).?\\5)|.")
Try it online!
\$\large\textit{Full programs}\$
Perl -p
, 42 38 37 bytes
$_=/^((.+)((.)(?3)\4|(?=\2$).?))?.?$/
Try it online!
Ruby -n
, 47 43 42 bytes
p~/^((.+)((.)\g<3>\k<4+0>|(?=\2$).?))?.?$/
Try it online!
Prints nil
for false and 0
for true, which are respectively falsey and truthy in Ruby. If printing 0
or 1
is desired, replace p~
with p !!
(+2 bytes).
PHP -F
, 63 59 58 bytes
<?=preg_match('/^((.+)((.)(?3)\4|(?=\2$).?))?.?$/',$argn);
Try it online! - bare-bones test harness
Try it online! - fancier test harness
3k+1
require both XY and YZ to overlap by exactly 1 char, and to me XYZ overlapping whenk=0
is a natural consequence of the rules. \$\endgroup\$abccbacbaabcabc
. My regex matches this if shortened to 45 bytes. \$\endgroup\$n
is it ann
lindrome?" \$\endgroup\$dadadadadadada
– I found a 51 byte Java/Perl/PCRE/.NET regex that passes all the test cases, but misidentifies that as a tralindrome. \$\endgroup\$