Making A Palindrome
September 29, 2015
Our algorithm works in two passes: First, run through the input accumulating frequency counts of each character. Second, generate the palindrome, or indicate that it is not possible to do so if more than one character occurs an odd number of times; since the problem didn’t specify, we order the characters of the string in increasing order before the center and decreasing order after it:
(define (palin str) (define (string-reverse str) (list->string (reverse (string->list str)))) (let ((freqs (make-vector 256 0))) (define (s t i d) (string-append t (make-string (/ (vector-ref freqs i) d) (integer->char i)))) (do ((cs (string->list str) (cdr cs))) ((null? cs)) (let ((i (char->integer (car cs)))) (vector-set! freqs i (+ (vector-ref freqs i) 1)))) (let loop ((i 0) (left "") (center "")) (cond ((= 256 i) (string-append left center (string-reverse left))) ((even? (vector-ref freqs i)) (loop (+ i 1) (s left i 2) center)) ((string=? center "") (loop (+ i 1) left (s center i 1))) (else #f))))) > (palin "pprrrraaxxxiiiiiissss") "aiiiprrssxxxssrrpiiia"
This isn’t very good from an efficiency standpoint. The call to string-append
is quadratic, and the algorithm as a whole generates a lot of garbage, not to mention the naive impolementation of string-reverse
. A better approach modifies the string in-place, once the frequencies are known:
(define (palin str) (let ((freqs (make-vector 256 0)) (len (string-length str))) (do ((cs (string->list str) (cdr cs))) ((null? cs)) (let ((i (char->integer (car cs)))) (vector-set! freqs i (+ (vector-ref freqs i) 1)))) (let loop ((i 0) (p 0) (center-char #f) (center-count 0)) (cond ((= 256 i) (when center-char (do ((j 0 (+ j 1))) ((= j center-count)) (string-set! str (+ p j) center-char))) str) ((even? (vector-ref freqs i)) (do ((j 0 (+ j 1))) ((= j (/ (vector-ref freqs i) 2))) (string-set! str (+ p j) (integer->char i)) (string-set! str (- len p j 1) (integer->char i))) (loop (+ i 1) (+ p (/ (vector-ref freqs i) 2)) center-char center-count)) ((not center-char) (loop (+ i 1) p (integer->char i) (vector-ref freqs i))) (else #f))))) > (palin "pprrrraaxxxiiiiiissss") "aiiiprrssxxxssrrpiiia"
You can run the program at http://ideone.com/f05O6W.
(Emacs Lisp / Common Lisp)
Simple solution using the loop macro
Same solution using named let
In Python.
In brute force. Python’s interaction engine doesn’t print the None that indicates impossibility.
Dumm and Smart in Scala:
Python. The dictionary ‘odd’ is used like a flip-flop to keep track of whether a pair of characters has been seen. If so, add the character to ‘half’; otherwise, put the character in ‘odd’ to indicate it needs a match. At the end, if there is no more than one character that still needs a match, then a palindrome if formed by concatenating ‘half’ with the character than needs a match, if any, and half reversed. If there are more than one character needing a match, then no palindrome is possible.
A Haskell version.
I am sure there would be an easier way to do this. But here is mine.
I decided to try to do it in java in as few lines of code as possible. Here’s what I got (13 lines):
import java.util.HashMap;
class Main {
public static void main(String[] args) {
String input = “aaaabbbccdd”;
int numOdd = 0;
HashMap numChars = new HashMap();
for (int k = 0; k 1 ? “Nope!” : “Yup!”);
}
}
looks like 4 of the lines got cut out. Not sure why
@Kevin, paste your Java code between sourcecode tag lines like the following, or look for other workable ways to post source code in the top bar of this site.
[sourcecode lang="java"]
[/sourcecode]
That should be two tag lines in square brackets. Your code between < and > confused a processor that was looking for either some HTML or else these square-bracketed tags (possibly other things that I don’t know about).
I enjoyed mike’s python solution. So I attempted to copy this concept to java
Here’s another Haskell solution – sort first, then scan the list looking for pairs. Reject if more that one unmatched item found:
“`
from itertools import permutations
def make_palindrome(word):
perms = list(permutations(word))
for i in perms:
if ”.join(i) == ”.join(i)[::-1]:
return ”.join(i)
return False
“`