21
\$\begingroup\$

Sometimes a string has buried palindromes in it:

hallolilah contains lol.

And if we took out lol, we'd be left with halilah...which is another palindrome.

Write a program/function that returns the minimum letters left over after repeatedly removing palindromes of at least 3 letters.

Possibly the order in which palindromes are removed affects the number of letters remaining, hence "minimum".

Input

A string of lowercase letters.

Output

A string of leftover lowercase letters.

Scoring

Code golf

Sample data

bazookabambino => bazookmbino

bamalamacocob => `` (empty string)

boofrooracecarnaanoorfood => bd

gogogogogogogogogonow => w

nopalindromesinthisone => nopalindromesinthisone

canacandothecancan => cadothecancan or andothecancan

acaaaab => b

\$\endgroup\$
8
  • 1
    \$\begingroup\$ Out of curiosity, how do you have so many challenges to post in such a short time? \$\endgroup\$ Commented Aug 1 at 7:43
  • \$\begingroup\$ I'd love some more suggested test cases. \$\endgroup\$ Commented Aug 1 at 11:24
  • 5
    \$\begingroup\$ @Rhaixer I save time by not coming up with any solutions. \$\endgroup\$ Commented Aug 1 at 11:25
  • \$\begingroup\$ I was thinking "This is missing the obvious scoring function by applying the function on itself and only counting what's left over", but then everyone would just submit a palindrome. \$\endgroup\$ Commented Aug 1 at 22:27
  • 1
    \$\begingroup\$ I think you have an ambiguous test case: canacandothecancan => andothecancan \$\endgroup\$ Commented Aug 3 at 2:08

12 Answers 12

9
\$\begingroup\$

Nekomata, 14 bytes

ʷ{;;$ƀ=tŁ¿,}aş 

Attempt This Online!

Extremely slow for long inputs that contain many palindromes.

ʷ{;;$ƀ=tŁ¿,}aş ʷ{ } Loop until failure ;; Split the input into three parts $ƀ= Check that the second part is a palindrome tŁ Check that the second part has length >= 3 ¿, If so, join the first and third parts; otherwise fail aş Find the shortest possible solution 

If there are multiple shortest solutions, Nekomata will print all of them. You can add the -1 flag to only print the first one.

\$\endgroup\$
7
\$\begingroup\$

JavaScript (ES6), 118 bytes

I/O format: arrays of characters.

f=(s,_=o=s)=>s.map((n,i)=>s.map(_=>(q=[...s],p=q.splice(i,n=-~n))[2]&&p+""==p.reverse()&&f(q,0)),s[o.length]?0:o=s)&&o 

Try it online!

Commented

f = ( // f is a recursive function taking: s, // s[] = input _ = o = s // o[] = output, initialized to the input ) => // s.map((n, i) => // for each character at index i in s[], // with n zero'ish: s.map(_ => // for each character in s[]: ( // q = [...s], // q[] = copy of s[] p = q.splice( // p[] = palindrome candidate i, // extracted from q[] at index i n = -~n // with a length of (at most) n ) // where n is incremented )[2] && // if p[] is at least 3 characters long p + "" == // and is a palindrome, i.e. it's equal p.reverse() && // to its reversed form: f(q, 0) // do a recursive call with q[] // (the 2nd argument is here only // to leave o[] unchanged) ), // end of inner map() s[o.length] ? // if s[] is longer than o[]: 0 // do nothing : // else: o = s // update o[] to s[] ) && o // end of outer map(); return o[] 
\$\endgroup\$
5
\$\begingroup\$

C64 Basic, 210 165 bytes

Here's a much shorter version of the original program. A big thanks to Robin from 8-Bit Show And Tell, who took a close look at my original code and was able to shorten it considerably.

0inputa$:j=1 1l=len(a$):i=j:j=l 2s=j-i:fOx=0tos:ifl<3ori=l-1tH?a$:eN 3s=s+(mI(a$,x+i,1)=mI(a$,j-x,1)):nE:ifs<0tHa$=leF(a$,i-1)+mI(a$,j+1):j=2 4j=j-1:on1-(j-1>i)gO1,2 

C64 screenshot

Try in emulator

\$\endgroup\$
5
  • \$\begingroup\$ I was just about to enter with a Commodore [Microsoft] BASIC listing, but you beat me to it. Good work. \$\endgroup\$ Commented Aug 8 at 16:08
  • \$\begingroup\$ @ShaunBebbington I would still like to see your listing. I am not convinced that my version is the most optimal. \$\endgroup\$ Commented Aug 10 at 12:32
  • \$\begingroup\$ in line 3 if j=i+1 then i=i+1 you can save 2 bytes by changing the assignment to i=j. \$\endgroup\$ Commented Aug 12 at 20:34
  • \$\begingroup\$ Ah, Robin of 8-bit Show and Tell had the same improvement. He also found a case where this program doesn't work: baaaaca should reduce to b (remove "aca" followed by "aaa"), not bca. \$\endgroup\$ Commented Aug 12 at 20:55
  • \$\begingroup\$ Wow, Robin did a remarkable job in size optimizing. Amazing, that I was still able to shorten Robin's code by one more byte. \$\endgroup\$ Commented Aug 14 at 16:05
4
\$\begingroup\$

Python3, 233 bytes

def f(s): q,r,S=[s],[],[s] for s in q: if[]==(o:=[(i,j)for i in range(len(s))for j in range(i+1,len(s)+1)if s[i:j]==s[i:j][::-1]and j-i>2]):r+=[s] for i,j in o: if(U:=s[:i]+s[j:])not in S:S+=[U];q+=[U] return min(r,key=len) 

Try it online!

\$\endgroup\$
1
  • \$\begingroup\$ 220 bytes \$\endgroup\$ Commented Oct 2 at 18:40
4
\$\begingroup\$

Retina, 83 bytes

+%/(.)+(.)\2?(?<-1>\1)+(?(1)^)/&L$w`(.)+(.)\2?(?<-1>\1)+(?(1)^) $`$' O#$` $.& L`^.* 

Try it online! Link includes faster¹ test cases. Explanation:

+%/(.)+(.)\2?(?<-1>\1)+(?(1)^)/&` 

Repeat on those lines that contain a palindrome...

L$w`(.)+(.)\2?(?<-1>\1)+(?(1)^) 

... for all overlapping matches...

$`$' 

... remove the match to give the resulting line.

O#$` $.& 

Sort the lines in ascending order of length.

L`^.* 

Output only the first (i.e. shortest) line.

¹gogogogogogogogogonow is too slow for the w style of overlapping match but the v style also happens to work in this case within TIO's time limit.

\$\endgroup\$
4
\$\begingroup\$

Python, 146 145 140 132 bytes

def f(s):L,*w=len(s),s;[s[a:b]==s[a:b][::-1]==(w:=w+[f(s[:a]+s[b:])])for a in range(L)for b in range(a+3,L+1)];return min(w,key=len) 

Attempt This Online!

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Save 5 bytes by putting a+3 as the lower limit of b's range so you don't have to check for b-a>2. \$\endgroup\$ Commented Aug 3 at 23:18
  • 1
    \$\begingroup\$ -8 more with listcomp (your last revision has no control flow, so can be inlined) and list shortcut (L,*w=len(s),s is shorter): ATO \$\endgroup\$ Commented Aug 8 at 14:53
2
\$\begingroup\$

JavaScript (Node.js), 214 bytes

f=s=>[...s,t=[]].map((_,i,a)=>[...Array(i).keys()].map(j=>j<i-2&&s.slice(j,(j+i)/2|0)==a.slice((j+i+1)/2|0,i).reverse().join``&&t.push(s.slice(0,j)+s.slice(i))))&&0 in t?t.map(f).sort((x,y)=>x.length-y.length)[0]:s 

Try it online!

Explanation

  • f=s=>...: Define a function f (named so recursion is possible) which solves the challenge for a string s
  • [...s,t=[]].map((_,i,a)=>...): For every integer i from 0 to the length of s, do the following... (with the characters of s stored in the array a)
    • [...Array(i).keys()].map(j=>j<i-2&&...): For every integer j from 0 to i-1, where j < i-2, do the following...
      • s.slice(j,(j+i)/2|0)==a.slice((j+i+1)/2|0,i).reverse().join``: Check if the slice of s ranging from j to i is a palindrome
      • ...&&t.push(s.slice(0,j)+s.slice(i)) If so, push the section of s without the palindrome to t
  • &&0 in t?...:s: Now, if t is empty, return s unchanged. Otherwise, one or more palindromes was found
  • t.map(f): For each palindrome found in s, recursively apply f to the parts of s without that palindrome
  • .sort((x,y)=>x.length-y.length)[0]: Choose the shortest result possible
\$\endgroup\$
2
\$\begingroup\$

Charcoal, 47 bytes

⊞υSFυFLιFEι✂ικμF∧›Lλ²⁼λ⮌λ⊞υΦι÷⁻ξκLλ§υ⌕EυLι⌊EυLι 

Try it online! Link is to verbose version of code. Explanation:

⊞υSFυ 

Start searching for palindromic substrings starting with the input.

FLιFEι✂ικμF∧›Lλ²⁼λ⮌λ 

If this substring is long enough and palindromic, then...

⊞υΦι÷⁻ξκLλ 

... filter it out from the string and push the result to the list of strings to test.

§υ⌕EυLι⌊EυLι 

Output the shortest resulting string.

\$\endgroup\$
2
\$\begingroup\$

Perl 5 -pl, 103 bytes

@,=$s=$_;/((.)(?1)\2|(.)(.)\4?\3)?(??{$1?push@,,$`.$':length$s>y!!!c?$s=$_:1;0})/,$_=pop@,while@,;$_=$s 

Try it online!

More readable:

@, = $s = $_; / ( (.)(?1)\2 | (.)(.)\4?\3 )? (??{ $1 ? push @,, $` . $' : length $s > y!!!c ? $s = $_ : 1; 0 }) /x, $_ = pop @, while @,; $_ = $s 

Perl 5, (non-competing) 458 bytes

$s = $_; %seen = (); while () { / (?>^.*?) ( ( (.)(?-3)\g-1|(.)(.)\g-1?\g-2 ) (?{ $x = substr( $_, 0, $-[2] ) . substr( $_ , $+[2] ); unless ( $seen{ $x } ++ ) { push @a, $x; $s = $x if length( $s ) > length( $x ) } }) ) /x; last unless @a; $_ = pop @a } $_ = $s; 

Try it online!

Short version is terribly inefficient. Just for fun, this is a much faster version (approx. 1 sec for 16 "go"'s in linked TIO's input vs. 9 in the challenge's test case), which hopefully generates correct results.

\$\endgroup\$
2
  • \$\begingroup\$ for the -6, this is what the "header" section of TIO is for: TIO \$\endgroup\$ Commented Aug 2 at 12:44
  • \$\begingroup\$ Looks like legit use case for "Header", will do as you advised, thanks \$\endgroup\$ Commented Aug 2 at 23:33
2
\$\begingroup\$

Pyth, 53 bytes

L&>lb2qb_bL{u+G|{m|s-Fdkm,dy#df.EyMT./H]HbYh.mlbu'G]w 

Try it online!

Explanation

Is b an eligible palindrome?

L # Declare a lambda `g(b)` … & # … which tests if both of the following are true: >lb2 # b is more than 2 characters long? qb_b # b is equal to the reversed form of itself? 

Remove a single palindrome

This lambda tries to find all the ways to remove a single palindrome. We accept a list of strings. For each string, we try to remove a single palindrome. If we can’t find any palindromes, then that means the string can’t be made smaller and is a potential winning candidate, so we keep it in the list. If we have a palindrome, then we remove it and keep the result. If we find more than one palindrome, we don’t know yet which one is best, so we remove each palindrome separately and keep all the results.

 Y # We’re going to use reduce, so start with an empty list. b # Our input, the list of strings. Let’s feed it into reduce. H # Maybe the string contains no palindromes. If so, keep it around … ] # … but for interface parity, let’s wrap it in a singleton list. H # With that out of the way, take the characters from our string … ./ # and explode it into a massive list of all possible substring partitions. yMT # For each substring, check whether it is a valid palindrome … .E # … and if that happens at least once in our partition, then … f # … keep the partition around, otherwise throw it away. y#d # In our right hand, we take the substrings that are palindromes … m,d # … in our left hand, we take the partition itself … -Fd # … and fold them into each other, eliminating the palindrome. # Conveniently, folding has removed each palindrome separately, # and multiple palindromes have automatically split into two separate # intermediate results. This is good, because we don’t know yet # which palindrome was the best one to remove, so let’s keep all the options. m s # Each result is still in form of a partition, so mend it back into a string. | k # Is our partition empty? If so, fix the mess that `s` has made with its default value 0. { # Our power-set shenanigans are causing abysmal performance, so let’s # deduplicate what we have to counteract the effect at least a little. | # If there were no palindromes at all, fall back to the singleton list we prepared earlier. u+G # Repeat for each input string and collect results. { # Deduplicate the resulting list once again … L # … and define a lambda named `'` so we can repeat all of the above over and over. 

Main routine

 w # Start with the input string … ] # … and wrap it inside a singleton list. That’s our initial state. 'G # From each list element, try removing a single palindrome … # … while keeping track of intermediate solutions … u # … over and over again, until our list no longer changes. # The list elements are now our potential winning candidates. lb # Look at the number of characters … .m # … of each candidate. Those with minimal length are our solutions. h # More than one solution? If so, pick the first one. 
\$\endgroup\$
1
\$\begingroup\$

C (clang), 212 199 148 140 133 121 118 112 bytes

i,j,m,n;f(*s){for(i=0;s[i];i++)for(j=wcslen(s);--j>i+1;)for(m=i,n=j;m>n?wcscpy(s+i,s-~j)<f(s):s[m++]==s[n--];);} 

Try it online!

-14 bytes thanks to @ceilingcat!

\$\endgroup\$
0
1
\$\begingroup\$

05AB1E, 23 bytes

¸ΔεDŒāÌù˜ʒÂQ}õªδK}˜ê}éн 

Try it online or verify all test cases.

Explanation:

¸ # Wrap the (implicit) input-string into a singleton list Δ # Loop until the result no longer changes: ε # Map over each inner string: D # Duplicate the current string Œ # Pop the copy, and get all its substrings āÌù˜ # Remove all substrings of lengths 1 and 2: ā # Push a list in the range [1,length] (without popping the list) Ì # Increase each value by 2: [3,length+2] ù # Map each value to the substrings of just that length ˜ # Flatten this list of lists ʒ # Filter the remaining strings: ÂQ # Check that the string is a palindrome:  # Bifurcate the string; short for Duplicate & Reverse copy Q # Check that the two strings are the same }õª # After the filter: append an empty string in case none are left δ # Map over each of the valid palindromes: K # Remove them from the duplicated current string }˜ # After the inner map: flatten the list of lists ê # (Sort and) uniquify this list of strings }é # After the changes-loop: sort the resulting strings by length н # Pop and keep the first/shortest one # (which is output implicitly as result) 
\$\endgroup\$

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.