Fork me on GitHub

Project Notes

#427 minSwapsToAlternate

Using Clojure to find the minimum swaps required to create an alternating array; cassidoo’s interview question of the week (2026-03-09).

Notes

The interview question of the week (2026-03-09) to find the minimum swaps required to create an alternating array:

Given a string s consisting only of ‘a’ and ‘b’, you may swap adjacent characters any number of times. Return the minimum number of adjacent swaps needed to transform s into an alternating string, either “ababab…” or “bababa…”, or return -1 if it’s impossible.

Example:

minSwapsToAlternate('aabb')
1

minSwapsToAlternate('aaab')
-1

minSwapsToAlternate('aaaabbbb')
6

Thinking about the Problem

Let:

  • na = count('a')
  • nb = count('b')

If |na - nb| > 1 then a solution is impossible.

Possible solutions:

  • start with a: “aba…”, or
  • start with b: “bab…”

But both are only valid if na == nb,

  • if na == nb + 1, then must start with a
  • if nb == na + 1, then must start with b

A first approach

We don’t need to actually perform swaps, just compute how far characters must move: the number of swaps required is the sum of the distances of characters from the current to required position.

I’m doing this in Clojure and it turns out to be quite elegant.

See source src/challenge.clj; there are no dependencies required:

(ns challenge
  (:gen-class))

(defn min-swaps-to-alternate [s]
  (let [n (count s)
        a-pos (keep-indexed #(when (= %2 \a) %1) s)
        na (count a-pos)
        nb (- n na)
        cost (fn [start]
               (reduce + (map #(Math/abs (- %1 %2))
                              a-pos
                              (range start (* 2 na) 2))))]
    (cond
      (> (Math/abs (- na nb)) 1) -1
      (> na nb) (cost 0)
      (> nb na) (cost 1)
      :else (min (cost 0) (cost 1)))))

(defn -main [& args]
  (let [s (first args)]
    (println (min-swaps-to-alternate s))))

Explaining the code:

  • starts by calculating the number of characters in the string, the number of a and b, and the list of current positions of a
  • core logic is in the cost helper:
    • given the start position for the first a
    • generates the target positions with range
    • uses map to compute, for each current position of a, the absolute distance to its intended position.
  • cond decision logic used to branch for:
    • impossible case
    • where more a than b
    • where more b than a
    • equal numbers of a and b (uses the min cost)

And the result is as expected:

$ clj -M -m challenge aabb
1
$ clj -M -m challenge aaab
-1
$ clj -M -m challenge aaaabbbb
6
$ clj -M -m challenge aaaabbbbbbaa
9

Credits and References

About LCK#427
Clojurecassidoo

This page is a web-friendly rendering of my project notes shared in the LittleCodingKata GitHub repository.

Project Source on GitHub Return to the LittleCodingKata Catalog
About LittleCodingKata

LittleCodingKata is my collection of programming exercises, research and code toys broadly spanning things that relate to programming and software development (languages, frameworks and tools).

These range from the trivial to the complex and serious. Many are inspired by existing work and I'll note credits and references where applicable. The focus is quite scattered, as I variously work on things new and important in the moment, or go back to revisit things from the past.

This is primarily a personal collection for my own edification and learning, but anyone who stumbles by is welcome to borrow, steal or reference the work here. And if you spot errors or issues I'd really appreciate some feedback - create an issue, send me an email or even send a pull-request.

Follow the Blog follow projects and notes as they are published in your favourite feed reader