C (gcc), 124 108 107 bytes
16 bytes saved thanks to c--! 1 byte saved thanks to ceilingcat!
f(c,n,i,j,k)char*c;{for(i=j=0;i<n;i++)j=*c-c[i]?j:i;k=c[i=j?n--,n--:i/2];
c[i]=c[j];c[j]=k;n>2&&f(c+!!j,n);}
Try it online! Linebreak added for clarity. Function f
which takes as input a pointer to the start of a char
array and its length as n
. Modifies the input array in place, yielding a single result.
Annoying that I can't save a few bytes by using c[i]^=c[j]^=c[i]^=c[j]
instead of a standard switch, but this expression fails when i == j
, and accounting for that doesn't end up saving any bytes.
Commented explanation
Slightly outdated, but the same general concept is the same. In the current version, we infer the count by observing that k
is 1 if and only if j
is 0.
f(c,n,i,j,k,t) char*c; {
// count the number of instances of the first character, *c
for(i = k = 0; i < n; i++)
// if we found *c in the string
*c == c[i]
? k++, j = i // then note it in our tally, and note its index as j
: 0; // else do nothing
// i is now the original length n
// j is now the index of the last occurrence of *c
// we will check if there is more than one occurrence of *c
--k
// this is truthy iff k > 1. in this case, we set up further recursion
? n -= 2, // deduct the two solved characters from the solve length
i-- // we want to swap with the end of the string (i=n-1)
// else, if k == 1, then we need to put this character in the middle
// to properly palindromize it
: (j = 0, // we want to swap the lone character (at j=0)
i /= 2); // with the center character (at i=n/2).
// swap characters at positions j and i
// when k>1, swaps the last occurrence of *c with the end of the string
// when k==1, swaps the first character with the middle of the string
t = c[i];
c[i] = c[j];
c[j] = t;
// if n < 2, the string is solved
// otherwise, we will recurse as follows:
// - when k was initially >1, k is now k-1, and !!k evaluates to 1,
// letting us recurse starting with c+1.
// in this case, n is now n-2, letting us recur on the string without
// the bookending characters
// - when k was initially 1, k is now 0, and !!k evaluates to 0.
// this means we recurse with c, and examine the character we
// just swapped there. n is also unchanged in this branch.
// furthermore, this swap only ever happens once because
// we check n > 2 before attempting to recurse.
n > 2 && f(c + !!k, n);
}
nanana
. \$\endgroup\$nnaaa
(a case where the odd-count element has a count greater than 1). \$\endgroup\$