In the popular (and essential) computer science book, An Introduction to Formal Languages and Automata by Peter Linz, the following formal language is frequently stated:


mainly because this language can not be processed with finite-state automata. This expression mean "Language L consists all strings of 'a's followed by 'b's, in which the number of 'a's and 'b's are equal and non-zero".


Write a working program/function which gets a string, containing "a"s and "b"s only, as input and returns/outputs a truth value, saying if this string is valid the formal language L.

  • Your program cannot use any external computation tools, including network, external programs, etc. Shells are an exception to this rule; Bash, e.g., can use command line utilities.

  • Your program must return/output the result in a "logical" way, for example: returning 10 instead of 0, "beep" sound, outputting to stdout etc. More info here.

  • Standard code golf rules apply.

This is a . Shortest code in bytes wins. Good luck!

Truthy test cases


Falsy test cases



Here is a Stack Snippet to generate both a regular leaderboard and an overview of winners by language.

/* Configuration */

var QUESTION_ID = 85994; // Obtain this from the url
// It will be like https://XYZ.stackexchange.com/questions/QUESTION_ID/... on any question page
var ANSWER_FILTER = "!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe";
var COMMENT_FILTER = "!)Q2B_A2kjfAiU78X(md6BoYk";
var OVERRIDE_USER = 48934; // This should be the user ID of the challenge author.

/* App */

var answers = [], answers_hash, answer_ids, answer_page = 1, more_answers = true, comment_page;

function answersUrl(index) {
  return "https://api.stackexchange.com/2.2/questions/" +  QUESTION_ID + "/answers?page=" + index + "&pagesize=100&order=desc&sort=creation&site=codegolf&filter=" + ANSWER_FILTER;

function commentUrl(index, answers) {
  return "https://api.stackexchange.com/2.2/answers/" + answers.join(';') + "/comments?page=" + index + "&pagesize=100&order=desc&sort=creation&site=codegolf&filter=" + COMMENT_FILTER;

function getAnswers() {
    url: answersUrl(answer_page++),
    method: "get",
    dataType: "jsonp",
    crossDomain: true,
    success: function (data) {
      answers.push.apply(answers, data.items);
      answers_hash = [];
      answer_ids = [];
      data.items.forEach(function(a) {
        a.comments = [];
        var id = +a.share_link.match(/\d+/);
        answers_hash[id] = a;
      if (!data.has_more) more_answers = false;
      comment_page = 1;

function getComments() {
    url: commentUrl(comment_page++, answer_ids),
    method: "get",
    dataType: "jsonp",
    crossDomain: true,
    success: function (data) {
      data.items.forEach(function(c) {
        if (c.owner.user_id === OVERRIDE_USER)
      if (data.has_more) getComments();
      else if (more_answers) getAnswers();
      else process();


var SCORE_REG = /<h\d>\s*([^\n,]*[^\s,]),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/;

var OVERRIDE_REG = /^Override\s*header:\s*/i;

function getAuthorName(a) {
  return a.owner.display_name;

function process() {
  var valid = [];
  answers.forEach(function(a) {
    var body = a.body;
    a.comments.forEach(function(c) {
        body = '<h1>' + c.body.replace(OVERRIDE_REG, '') + '</h1>';
    var match = body.match(SCORE_REG);
    if (match)
        user: getAuthorName(a),
        size: +match[2],
        language: match[1],
        link: a.share_link,
  valid.sort(function (a, b) {
    var aB = a.size,
        bB = b.size;
    return aB - bB

  var languages = {};
  var place = 1;
  var lastSize = null;
  var lastPlace = 1;
  valid.forEach(function (a) {
    if (a.size != lastSize)
      lastPlace = place;
    lastSize = a.size;
    var answer = jQuery("#answer-template").html();
    answer = answer.replace("{{PLACE}}", lastPlace + ".")
                   .replace("{{NAME}}", a.user)
                   .replace("{{LANGUAGE}}", a.language)
                   .replace("{{SIZE}}", a.size)
                   .replace("{{LINK}}", a.link);
    answer = jQuery(answer);

    var lang = a.language;
    if (/<a/.test(lang)) lang = jQuery(lang).text();
    languages[lang] = languages[lang] || {lang: a.language, user: a.user, size: a.size, link: a.link};

  var langs = [];
  for (var lang in languages)
    if (languages.hasOwnProperty(lang))

  langs.sort(function (a, b) {
    if (a.lang > b.lang) return 1;
    if (a.lang < b.lang) return -1;
    return 0;

  for (var i = 0; i < langs.length; ++i)
    var language = jQuery("#language-template").html();
    var lang = langs[i];
    language = language.replace("{{LANGUAGE}}", lang.lang)
                       .replace("{{NAME}}", lang.user)
                       .replace("{{SIZE}}", lang.size)
                       .replace("{{LINK}}", lang.link);
    language = jQuery(language);

body { text-align: left !important}

#answer-list {
  padding: 10px;
  width: 290px;
  float: left;

#language-list {
  padding: 10px;
  width: 290px;
  float: left;

table thead {
  font-weight: bold;

table td {
  padding: 5px;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b">
<div id="answer-list">
  <table class="answer-list">
    <tbody id="answers">

<div id="language-list">
  <h2>Winners by Language</h2>
  <table class="language-list">
    <tbody id="languages">

<table style="display: none">
  <tbody id="answer-template">
    <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr>
<table style="display: none">
  <tbody id="language-template">
    <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr>

  • 24
    \$\begingroup\$ Can the input be empty? (You're saying it's not part of the language, but not whether it's an input we need to consider.) \$\endgroup\$ Jul 20, 2016 at 20:04
  • 1
    \$\begingroup\$ What if our language doesn't have truthy or falsy? Would empty string == truthy and non-empty string == falsy be acceptable? \$\endgroup\$
    – DJMcMayhem
    Jul 20, 2016 at 20:20
  • 5
    \$\begingroup\$ Nice challenge, but I think the title could be a little less ambiguous (i.e. a mention of a^n b^n or similar, rather than just the number of as equalling the number of bs) \$\endgroup\$
    – Sp3000
    Jul 21, 2016 at 12:28
  • 1
    \$\begingroup\$ @Sp3000 I choosed this title because it looked fun . I may change it later to sth else ... \$\endgroup\$
    – user55673
    Jul 21, 2016 at 13:27
  • 2
    \$\begingroup\$ I'm a little surprised that in 50+ answers I'm the only one to use a paser generator. To be sure it's not strictly competitive on length, but the problem posed is one of parsing a simple but non-trivial language. I'd very much like to see answers in other compiler-compiler syntaxes because I am not widely familiar with the choices. \$\endgroup\$ Jul 22, 2016 at 22:47

95 Answers 95


Python 3, 32 bytes


Outputs via exit code: Error for false, no error for True.

The string is evaluated as Python code, replacing parens ( for a and ) for b. Only expressions of the form a^n b^n become well-formed expressions of parentheses like ((())), evaluating to the tuple ().

Any mismatched parens give an error, as will multiple groups like (()()), since there's no separator. The empty string also fails (it would succeed on exec).

The conversion ( -> a, ) -> b is done using str.translate, which replaces characters as indicated by a string that serves as a conversion table. Given the 100-length string ")("*50, the tables maps the first 100 ASCII values as

... Z[\]^_`abc
... )()()()()(

which takes ( -> a, ) -> b. In Python 2, conversions for all 256 ASCII values must be provided, requiring "ab"*128, one byte longer; thanks to isaacg for pointing this out.

  • 59
    \$\begingroup\$ Ok, that's clever. \$\endgroup\$
    – TLW
    Jul 21, 2016 at 2:03
  • \$\begingroup\$ What does the *128 do? \$\endgroup\$ Jul 22, 2016 at 22:17
  • 6
    \$\begingroup\$ 128 can be replaced by 50 (or 99 for that matter) to save a byte. \$\endgroup\$
    – isaacg
    Jul 22, 2016 at 23:40
  • \$\begingroup\$ @Eʀɪᴋ ᴛʜᴇ Gᴏʟғᴇʀ: I think it´s a quantifier. But I don´t really know Python and have not found any documentation on that yet. \$\endgroup\$
    – Titus
    Jul 23, 2016 at 3:02
  • 4
    \$\begingroup\$ @isaacg Thanks, I wasn't aware that changed for Python 3. \$\endgroup\$
    – xnor
    Jul 23, 2016 at 6:43

MATL, 5 4 bytes


Prints a non-empty array of 1s if the string belongs to L, and an empty array or an array with 0s (both falsy) otherwise.

Thanks to @LuisMendo for golfing off 1 byte!

Try it online!

How it works

t      Push a copy of the implicitly read input.
 S     Sort the copy.
  P    Reverse the sorted copy.
   -   Take the difference of the code point of the corresponding characters
       of the sorted string and the original.
  • 7
    \$\begingroup\$ My second (working) MATL answer. :) \$\endgroup\$
    – Dennis
    Jul 21, 2016 at 0:49
  • 2
    \$\begingroup\$ Strange definition of truthy and falsy: 'aabb' gives -1 -1 1 1 is truthy. 'aaabb' gives -1 -1 0 1 1 and is falsy \$\endgroup\$
    – Etoplay
    Jul 25, 2016 at 8:02
  • 3
    \$\begingroup\$ @Etoplay A non-empty array with all its values nonzero is truthy. It's the definition used in Matlab and Octave \$\endgroup\$
    – Luis Mendo
    Jul 25, 2016 at 12:36

Retina, 12 bytes

Credits to FryAmTheEggman who found this solution independently.


Prints 1 for valid input and 0 otherwise.

Try it online! (The first line enables a linefeed-separated test suite.)


Balancing groups require expensive syntax, so instead I'm trying to reduce a valid input to a simple form.

Stage 1


The + tells Retina to repeat this stage in a loop until the output stops changing. It matches either ab or a;b and replaces it with ;. Let's consider a few cases:

  • If the as and the bs in the string aren't balanced in the same way that ( and ) normally need to be, some a or b will remain in the string, since ba, or b;a can't be resolved and a single a or b on its own can't either. To get rid of all the as and the bs there has to be one corresponding b to the right of each a.
  • If the a and the b aren't all nested (e.g. if we have something like abab or aabaabbb) then we'll end up with multiple ; (and potentially some as and bs) because the first iteration will find multiple abs to insert them and further iterations will preserve the number of ; in the string.

Hence, if and only if the input is of the form anbn, we'll end up with a single ; in the string.

Stage 2:


Check whether the resulting string contains nothing but a single semicolon. (When I say "check" I actually mean, "count the number of matches of the given regex, but since that regex can match at most once due to the anchors, this gives either 0 or 1.)


Haskell, 31 bytes

f s=s==[c|c<-"ab",'a'<-s]&&s>""

The list comprehension [c|c<-"ab",'a'<-s] makes a string of one 'a' for each 'a' in s, followed by one 'b' for each 'a' in s. It avoids counting by matching on a constant and producing an output for each match.

This string is checked to be equal to the original string, and the original string is checked to be non-empty.

  • \$\begingroup\$ This is lovely. I often forget how useful it is that Haskell orders the elements of a list comprehension in a consistent and very specific way. \$\endgroup\$
    – Vectornaut
    Jul 21, 2016 at 19:31
  • \$\begingroup\$ Much nicer than my best attempt (f=g.span id.map(=='a');g(a,b)=or a&&b==(not<$>a)) . Well done. \$\endgroup\$
    – Jules
    Jul 22, 2016 at 21:04
  • \$\begingroup\$ Wow, I didn't know one could match on a constant in a list comprehension! \$\endgroup\$
    – rubik
    Jul 24, 2016 at 7:41

Grime, 12 bytes


Try it online!


The first line defines a nonterminal A, which matches one letter a, possibly the nonterminal A, and then one letter b. The second line matches the entire input (e) against the nonterminal A.

8-byte noncompeting version


After writing the first version of this answer, I updated Grime to consider _ as the name of the top-level expression. This solution is equivalent to the above, but avoids repeating the label A.

  • \$\begingroup\$ Why didn't you do it in J? \$\endgroup\$
    – Leaky Nun
    Jul 20, 2016 at 19:38
  • \$\begingroup\$ @LeakyNun I just wanted to show off Grime. :P \$\endgroup\$
    – Zgarb
    Jul 20, 2016 at 19:39
  • \$\begingroup\$ You built this language? \$\endgroup\$
    – Leaky Nun
    Jul 20, 2016 at 19:40
  • \$\begingroup\$ @LeakyNun Yes. Development is slow, but ongoing. \$\endgroup\$
    – Zgarb
    Jul 20, 2016 at 19:41

Brachylog, 23 19 bytes


Try it online!


@2L,                  Split the input in two, the list containing the two halves is L
    ?lye              Take a number I between 0 and the length of the input              
        :"ab"rz       Zip the string "ab" with that number, resulting in [["a":I]:["b":I]]
               :jaL   Apply juxtapose with that zip as input and L as output
                        i.e. "a" concatenated I times to itself makes the first string of L
                        and "b" concatenated I times to itself makes the second string of L
  • 8
    \$\begingroup\$ Congratulations on getting on tryitonline.net! \$\endgroup\$
    – Leaky Nun
    Jul 20, 2016 at 20:01

05AB1E, 9 bytes




.M         # Get the most frequent element from the input. If the count is equal, this
           results into ['a', 'b'] or ['b', 'a'].
  {        # Sort this list, which should result into ['a', 'b'].
   J       # Join this list.
    Ô      # Connected uniquified. E.g. "aaabbb" -> "ab" and "aabbaa" -> "aba".
     Q     # Check if both strings are equal.
      0r   # (Print 0 if the input is empty).

The last two bytes can be discarded if the input is guaranteed to be non-empty.

Uses the CP-1252 encoding. Try it online!.

  • \$\begingroup\$ What happens with empty input? \$\endgroup\$ Jul 20, 2016 at 19:54
  • 2
    \$\begingroup\$ Look for non-zero in the post; it’s in there :) \$\endgroup\$
    – Lynn
    Jul 20, 2016 at 20:01
  • \$\begingroup\$ @Lynn Doesn't the spec only say no-zero for a valid language though? Not about input. \$\endgroup\$
    – Emigna
    Jul 20, 2016 at 20:02
  • \$\begingroup\$ True. Thought wrong there. But you can still do .M{J¹ÔQ0r for yours. \$\endgroup\$
    – Emigna
    Jul 20, 2016 at 20:17
  • \$\begingroup\$ @Emigna Thanks, I have edited the post. \$\endgroup\$
    – Adnan
    Jul 20, 2016 at 20:20

Bison/YACC 60 (or 29) bytes

(Well, the compilation for a YACC program is a couple of steps so might want to include some for that. See below for details.)

yylex(){return getchar();}

The function should be fairly obvious if you know to interpret it in terms of a formal grammar. The parser accepts either ab or an a followed by any acceptable sequence followed by a b.

This implementation relies on a compiler that accepts K&R semantics to lose a few characters.

It's wordier than I would like with the need to define yylex and to call getchar.

Compile with

$ yacc equal.yacc
$ gcc -m64 --std=c89 y.tab.c -o equal -L/usr/local/opt/bison/lib/ -ly

(most of the options to gcc are specific to my system and shouldn't count against the byte count; you might want to count -std=c89 which adds 8 to the value listed).

Run with

$ echo "aabb" | ./equal

or equivalent.

The truth value is returned to the the OS and errors also report syntax error to the command line. If I can count only the part of the code that defines the parsing function (that is neglect the second %% and all that follows) I get a count of 29 bytes.


Jelly, 6 bytes


Prints the string itself if it belongs to L or is empty, and 0 otherwise.

Try it online! or verify all test cases.

How it works

Ṣ=Ṛ¬Pȧ  Main link. Argument: s (string)

Ṣ       Yield s, sorted.
  Ṛ     Yield s, reversed.
 =      Compare each character of sorted s with each character of reversed s.
   ¬    Take the logical NOT of each resulting Boolean.
    P   Take the product of the resulting Booleans.
        This will yield 1 if s ∊ L or s == "", and 0 otherwise.
     ȧ  Take the logical AND with s.
       This will replace 1 with s. Since an empty string is falsy in Jelly,
       the result is still correct if s == "".

Alternate version, 4 bytes (non-competing)


Prints 1 or 0. Try it online! or verify all test cases.

How it works

ṢnṚȦ  Main link. Argument: s (string)

Ṣ     Yield s, sorted.
  Ṛ   Yield s, reversed.
 n    Compare each character of the results, returning 1 iff they're not equal.
   Ȧ  All (Octave-style truthy); return 1 if the list is non-empty and all numbers
      are non-zero, 0 in all other cases.

J, 17 bytes


This works correctly for giving falsey for the empty string. Error is falsey.

Old versions:

2&#-:'ab'#~#   NB. thanks to miles

Proof and explanation

The main verb is a fork consisting of these three verbs:

# <. (-:'ab'#~-:@#)

This means, "The lesser of (<.) the length (#) and the result of the right tine ((-:'ab'#~-:@#))".

The right tine is a 4-train, consisting of:

(-:) ('ab') (#~) (-:@#)

Let k represent our input. Then, this is equivalent to:

k -: ('ab' #~ -:@#) k

-: is the match operator, so the leading -: tests for invariance under the monadic fork 'ab' #~ -:@#.

Since the left tine of the fork is a verb, it becomes a constant function. So, the fork is equivalent to:

'ab' #~ (-:@# k)

The right tine of the fork halves (-:) the length (#) of k. Observe #:

   1 # 'ab'
   2 # 'ab'
   3 # 'ab'
   'ab' #~ 3

Now, this is k only on valid inputs, so we are done here. # errors for odd-length strings, which never satisfies the language, so there we are also done.

Combined with the lesser of the length and this, the empty string, which is not a part of our language, yields its length, 0, and we are done with it all.

  • \$\begingroup\$ I modified it into 2&#-:'ab'#~# which should let you avoid the error and just output 0 instead while still using 12 bytes. \$\endgroup\$
    – miles
    Jul 20, 2016 at 21:33
  • \$\begingroup\$ @miles Fascinating! I never thought about it like that. \$\endgroup\$ Jul 20, 2016 at 21:39
  • \$\begingroup\$ Does this handle the empty string? \$\endgroup\$
    – Zgarb
    Jul 21, 2016 at 11:24
  • \$\begingroup\$ @Zgarb fixed it! \$\endgroup\$ Jul 21, 2016 at 19:30

JavaScript, 54 55 44


Builds a simple regex based on the length of the string and tests it. For a length 4 string (aabb) the regex looks like: ^a{2}b{2}$

Returns a truthy or falsey value.

11 bytes saved thanks to Neil.

// true
console.log(f('ab'), !!f('ab'))
console.log(f('aabb'), !!f('aabb'))
console.log(f('aaaaabbbbb'), !!f('aaaaabbbbb'))
// false
console.log(f('a'), !!f('a'))
console.log(f('b'), !!f('b'))
console.log(f('ba'), !!f('ba'))
console.log(f('aaab'), !!f('aaab'))
console.log(f('ababab'), !!f('ababab'))
console.log(f('c'), !!f('c'))
console.log(f('abc'), !!f('abc'))
console.log(f(''), !!f(''))

  • \$\begingroup\$ The f= can be omitted. \$\endgroup\$
    – Leaky Nun
    Jul 20, 2016 at 19:53
  • \$\begingroup\$ Is a function expression a valid submission, or must it actually be, ahem, functional? \$\endgroup\$
    – Scimonster
    Jul 20, 2016 at 19:54
  • \$\begingroup\$ A function is a valid submission. \$\endgroup\$
    – Leaky Nun
    Jul 20, 2016 at 19:56
  • \$\begingroup\$ @TimmyD It used to return true, but now it returns false. \$\endgroup\$
    – Scimonster
    Jul 20, 2016 at 20:06
  • 1
    \$\begingroup\$ s=>s.match(`^a{${s.length/2}}b+$`)? \$\endgroup\$
    – l4m2
    May 30, 2018 at 4:36

Perl 5.10, 35 17 bytes (with -n flag)


Ensures that the string starts with as and then recurses on bs. It matches only if both lengths are equal.

Thanks to Martin Ender for halving the byte count and teaching me a little about recursion in regexes :D

It returns the whole string if it matches, and nothing if not.

Try it here!

  • \$\begingroup\$ Closest I can manage including the non-empty test case is 18 bytes: $_&&=y/a//==y/b// (requires -p), without the empty you could drop the && for 16! So close... \$\endgroup\$ Jul 21, 2016 at 9:08
  • 1
    \$\begingroup\$ So I can do another 17 bytes: echo -n 'aaabbb'|perl -pe '$_+=y/a//==y/b//' but I can't shift another byte... Might have to give up on this! \$\endgroup\$ Jul 22, 2016 at 15:33
  • \$\begingroup\$ Just realised that this could be done in 7 bytes using that regex as -F/^(a(?1)?b)$/: tio.run/##LYw9CoVADIT7nMNCCxGLV/pTeQwlAQtBdFHPb9yZPFgyk518k9Zr/… \$\endgroup\$ Apr 17, 2020 at 10:51

C, 57 53 Bytes


Old 57 bytes long solution:


Compiled with gcc v. 4.8.2 @Ubuntu

Thanks ugoren for tips!

Try it on Ideone!

  • \$\begingroup\$ Since I'm new here and can't comment on other answers yet, I just want to point out that 62b solution from @Josh gives false positive on strings like "aaabab". \$\endgroup\$
    – Jasmes
    Jul 22, 2016 at 15:47
  • \$\begingroup\$ Change (t+=2) to t++++ for -1 byte. \$\endgroup\$
    – owacoder
    Jul 25, 2016 at 13:20
  • 2
    \$\begingroup\$ @owacoder t++++ is not a valid C code. \$\endgroup\$
    – Jasmes
    Jul 25, 2016 at 14:21
  • \$\begingroup\$ Save some with t+=*s%2*2 and :*s*!1[s] \$\endgroup\$
    – ugoren
    Jul 31, 2016 at 14:18
  • \$\begingroup\$ Very clever answer! It does unfortunately fail on input "ba" : ideone.com/yxixG2 \$\endgroup\$
    – Josh
    Aug 8, 2016 at 15:07

Retina, 22 bytes

Another shorter answer in the same language just came...


Try it online!

This is a showcase of the balancing groups in regex, which is explained fully by Martin Ender.

As my explanation would not come close to half of it, I'll just link to it and not attempt to explain, as that would be detrimental to the glory of his explanation.


Befunge-93, 67 bytes

0v@.<  0<@.!-$<  >0\v
+>~:0`!#^_:"a" -#^_$ 1
~+1_^#!-"b" _ ^#`0: <

Try it here! Might explain how it works later. Might also try to golf it just a tad bit more, just for kicks.


MATL, 9 bytes


Try it online!

The output array is truthy if it is non-empty and all its entries are nonzero. Otherwise it's falsy. Here are some examples.

v     % concatenate the stack. Since it's empty, pushes the empty array, []
H     % push 2
I$    % specify three inputs for next function
e     % reshape(input, [], 2): this takes the input implicitly and reshapes it in 2
      % columns in column major order. If the input has odd length a zero is padded at
      % the end. For input 'aaabbb' this gives the 2D char array ['ab;'ab';'ab']
!     % transpose. This gives ['aaa;'bbb']
d     % difference along each column
1=    % test if all elements are 1. If so, that means the first tow contains 'a' and
      % the second 'b'. Implicitly display
  • 2
    \$\begingroup\$ That's some convenient definition of truthy. (I knew about the non-zero requirement, but not about the non-empty one.) \$\endgroup\$
    – Dennis
    Jul 21, 2016 at 0:34

x86 machine code, 29 27 bytes


33 c0 40 41 80 79 ff 61 74 f8 48 41 80 79 fe 62
74 f8 0a 41 fe f7 d8 1b c0 40 c3

Assembly code:

    xor eax, eax;
    inc eax;
    inc ecx;
    cmp byte ptr [ecx-1], 'a';
    je loop1;

    dec eax;
    inc ecx;
    cmp byte ptr [ecx-2], 'b';
    je loop2;

    or al, [ecx-2];
    neg eax;
    sbb eax, eax;
    inc eax;

Iterates over the a bytes in the beginning, then over the following 'b' bytes. The first loop increases a counter, and the second loop decreases it. Afterwards, does a bitwise OR between the following conditions:

  1. If the counter is not 0 at the end, the string doesn't match
  2. If the byte that follows the sequence of bs is not 0, the string also doesn't match

Then, it has to "invert" the truth value in eax - set it to 0 if it was not 0, and vice versa. It turns out that the shortest code to do that is the following 5-byte code, which I stole from the output of my C++ compiler for result = (result == 0):

    neg eax;      // negate eax; set C flag to 1 if it was nonzero
    sbb eax, eax; // subtract eax and the C flag from eax
    inc eax;      // increase eax
  • 1
    \$\begingroup\$ I think you can improve your negation. Try: neg eax to set the carry flag as before, cmc to invert the carry flag and salc to set AL to FFh or 0 depending on whether the carry flag is set or not. Saves 2 bytes, although does end up with an 8-bit result rather than 32-bit. \$\endgroup\$
    – Jules
    Jul 22, 2016 at 21:40
  • \$\begingroup\$ The same thing using string ops, with ESI pointing to the input string, and returning the result in AL (uses SETcc, requires 386+): xor eax,eax | xor ecx,ecx | l1: inc ecx | lodsb | cmp al, 'a' | jz l1 | dec esi | l2: lodsb | cmp al,'b' | loopz l2 | or eax,ecx | setz al | ret \$\endgroup\$
    – ninjalj
    Jul 26, 2016 at 21:26
  • \$\begingroup\$ @ninjalj You should post that in an answer - it's sufficiently different from mine, and I suspect is significantly shorter! \$\endgroup\$
    – anatolyg
    Jul 26, 2016 at 22:02

Ruby, 24 bytes


(This is just xnor's brilliant idea in Ruby form. My other answer is a solution I actually came up with myself.)

The program takes the input, transforms a and b to [ and ] respectively, and evaluates it.

Valid input will form a nested array, and nothing happens. An unbalanced expression will make the program crash. In Ruby empty input is evaluated as nil, which will crash because nil has not defined a * method.


Sed, 38 + 2 = 40 bytes


A non empty string output is truthy

Finite state automata can not do this, you say? What about finite state automata with loops. :P

Run with r and n flags.


s/.*/c&d/        #Wrap the input in 'c' and 'd' (used as markers)
:x               #Define a label named 'x'
s/ca(.*)bd/c\1d/ #Deletes 'a's preceded by 'c's and equivalently for 'b's and 'd's. This shifts the markers to the center
tx               #If the previous substitution was made, jump to label x
/cd/p            #If the markers are next to one another, print the string
  • \$\begingroup\$ Nice approach. Thanks for the breakdown. \$\endgroup\$ Jul 25, 2016 at 3:28

JavaScript, 44 42

Crossed out 44 is still regular 44 ;(


Works by recursively stripping off the outer a and b and recursively using the inner value selected but .+. When there's no match on ^a.+b$ left, then the final result is whether the remaining string is the exact value ab.

Test cases:

console.log(["ab","aabb","aaabbb","aaaabbbb","aaaaabbbbb","aaaaaabbbbbb"].every(f) == true)
console.log(["","a","b","aa","ba","bb","aaa","aab","aba","abb","baa","bab","bba","bbb","aaaa","aaab","aaba","abaa","abab","abba","abbb","baaa","baab","baba","babb","bbaa","bbab","bbba","bbbb"].some(f) == false)

ANTLR, 31 bytes

grammar A;r:'ab'|'a'r'b'|r'\n';

Uses the same concept as @dmckee's YACC answer, just slightly more golfed.

To test, follow the steps in ANTLR's Getting Started tutorial. Then, put the above code into a file named A.g4 and run these commands:

$ antlr A.g4
$ javac A*.java

Then test by giving input on STDIN to grun A r like so:

$ echo "aaabbb" | grun A r

If the input is valid, nothing will be output; if it is invalid, grun will give an error (either token recognition error, extraneous input, mismatched input, or no viable alternative).

Example usage:

$ echo "aabb" | grun A r
$ echo "abbb" | grun A r
line 1:2 mismatched input 'b' expecting {<EOF>, '
  • \$\begingroup\$ Clever trick adding the newline as an alternate in a single rule. I think I could save a few that way in yacc, too. The grammer keyword is a stinker for golfing with antlr, though. Kinda like using fortran. \$\endgroup\$ Jul 28, 2016 at 16:15

C, 69 bytes

69 bytes:

#define f(s)strlen(s)==2*strcspn(s,"b")&strrchr(s,97)+1==strchr(s,98)

For those unfamiliar:

  • strlen determines the length of the string
  • strcspn returns the first index in string where the other string is found
  • strchr returns a pointer to the first occurrence of a character
  • strrchr returns a pointer to the last occurrence of a character

A big thanks to Titus!

  • 1
    \$\begingroup\$ save one byte with >97 instead of ==98 \$\endgroup\$
    – Titus
    Jul 23, 2016 at 2:02
  • 2
    \$\begingroup\$ 61 bytes solution gives false positive on strings like "aaabab". See ideone.com/nmT8rm \$\endgroup\$
    – Jasmes
    Jul 28, 2016 at 16:36
  • \$\begingroup\$ Ah you are correct Jasmes, thanks. I will have to rethink this a little. \$\endgroup\$
    – Josh
    Jul 28, 2016 at 17:30
  • \$\begingroup\$ Reverting back to the 69 byte solution, not sure if I can get any shorter using this approach. \$\endgroup\$
    – Josh
    Jul 28, 2016 at 19:45

R, 64 61 55 bytes, 73 67 bytes (robust) or 46 bytes (if empty strings are allowed)

  1. Again, xnor's answer reworked. If it is implied by the rules that the input will consist of a string of as and bs, it should work: returns NULL if the expression is valid, throws and error or nothing otherwise.

  2. If the input is not robust and may contain some garbage, e.g. aa3bb, then the following version should be considered (must return TRUE for true test cases, not NULL):

  3. Finally, if empty strings are allowed, we can ignore the condition for non-empty input:


    Again, NULL if success, anything else otherwise.

  • \$\begingroup\$ I don´t know R, what´s your result for empty input? (should be falsy) \$\endgroup\$
    – Titus
    Jul 23, 2016 at 2:35
  • \$\begingroup\$ Is there really no shorter way to test for empty input? \$\endgroup\$
    – Titus
    Jul 25, 2016 at 6:58
  • \$\begingroup\$ Version 1: just nothing (correct input returns only NULL), version 2: just nothing (correct input returns only TRUE), version 3 (assuming empty strings are OK, as state): NULL. R is a statistical object-oriented language that typecasts everything just OK, without any warning. \$\endgroup\$ Jul 26, 2016 at 11:24
  • \$\begingroup\$ This (answer 1) can be further improved to 55 bytes: if((y<-scan(,''))>'')eval(parse(t=chartr('ab','{}',y))) \$\endgroup\$
    – Giuseppe
    Aug 4, 2017 at 14:01

Japt, 11 bytes

©¬n eȦUg~Y

Try it online!

Gives either true or false, except that "" gives "" which is falsy in JS.

Unpacked & How it works

U&&Uq n eXYZ{X!=Ug~Y

U&&     The input string is not empty, and...
Uq n    Convert to array of chars and sort
eXYZ{   Does every element satisfy...?
X!=       The sorted char does not equal...
Ug~Y      the char at the same position on the original string reversed

Adopted from Dennis' MATL solution.


Brachylog (newer), 11 10 6 bytes


Try it online!

The predicate succeeds if the input is in L and fails otherwise.

          The input
o         sorted
 ?        is the input,
  ḅ       and the runs in the input
   Ċ      of which there are two
    lᵛ    have the same length.
  • 1
    \$\begingroup\$ At this rate you will catch up to my number of Brachylog answers in a few days! \$\endgroup\$
    – Fatalize
    Mar 1, 2019 at 12:17
  • 1
    \$\begingroup\$ That use of is really clever! \$\endgroup\$
    – DLosc
    Feb 16 at 22:32
  • \$\begingroup\$ @DLosc Thanks! z₂ didn't exist yet ;) \$\endgroup\$ Feb 16 at 23:09

05AB1E, 6 bytes


Only 1 is truthy in 05AB1E, and it'll output 0 (or "" for the empty string) as falsey.

Try it online or verify all test cases.


Ç       # Convert the (implicit) input-string to a list of integer codepoints
        #  i.e. "aaabbb" → [97,97,97,98,98,98]
        #  i.e. "baba" → [98,97,98,97]
        #  i.e. "aab" → [97,97,98]
        #  i.e. "" → []
 Â      # Bifurcate this list (short for Duplicate & Reverse copy)
        #  STACK: [[97,97,97,98,98,98], [98,98,98,97,97,97]]
        #  STACK: [[98,97,98,97], [97,98,97,98]]
        #  STACK: [[97,97,98], [98,97,97]]
        #  STACK: [[], []]
  s     # Swap to get the duplicated list
        #  STACK: [[98,98,98,97,97,97], [97,97,97,98,98,98]]
        #  STACK: [[97,98,97,98], [98,97,98,97]]
        #  STACK: [[98,97,97], [97,97,98]]
        #  STACK: [[], []]
   {    # Sort it
        #  STACK: [[98,98,98,97,97,97], [97,97,97,98,98,98]]
        #  STACK: [[97,98,97,98], [97,97,98,98]]
        #  STACK: [[98,97,97], [97,97,98]]
        #  STACK: [[], []]
    α   # Take the absolute difference at the same positions
        #  STACK: [[1,1,1,1,1,1]]
        #  STACK: [[0,1,1,0]]
        #  STACK: [[1,0,1]]
        #  STACK: [[], []]
     ß  # And take the minimum, which will be 1 if all were truthy;
        # 0 if any were falsey; or an empty string if the list is empty
        #  STACK: [1]
        #  STACK: [0]
        #  STACK: [0]
        #  STACK: [""]
        # (after which it is output implicitly as result)

C (Ansi), 65 75 Bytes


l(b,i,j,k)char*b;{for(i=j=0;(k=b[i++])>0&k<=b[i];)j+=2*(k>97)-1;return !j;}


Sets a value j and increments j on each b and decrements j on anything else. Checked if the letter before is less than or equal the next letter so prevent abab from working


Added checks for abab cases.

  • \$\begingroup\$ Won't this give false positives on strings like ba or abab? \$\endgroup\$
    – Zgarb
    Jul 20, 2016 at 19:43
  • \$\begingroup\$ Ahh yes, I misread the post since I could not see the picture since its blocked for me. Fixing it! \$\endgroup\$
    – dj0wns
    Jul 20, 2016 at 19:50

Batch, 133 bytes

@if ""=="%1" exit/b1        Fail if the input is empty
@set a=%1                   Grab the input into a variable for processing
@set b=%a:ab=%              Remove all `ab` substrings
@if "%a%"=="%b%" exit/b1    Fail if we didn't remove anything
@if not %a%==a%b%b exit/b1  Fail if we removed more than one `ab`
@if ""=="%b%" exit/b0       Success if there's nothing left to check
@%0 %b%                     Rinse and repeat

Returns an ERRORLEVEL of 0 on success, 1 on failure. Batch doesn't like to do substring replacement on empty strings, so we have to check that up front; if an empty parameter was legal, line 6 wouldn't be necessary.


PowerShell v2+, 61 52 bytes


Takes input $n as a string, creates $x as half the length. Constructions an -and Boolean comparison between $n and a -match regex operator against the regex of an equal number of a's and b's. Outputs Boolean $TRUE or $FALSE. The $n-and is there to account for ""=$FALSE.

Alternate, 35 bytes


This uses the regex from Leaky's answer, based on .NET balancing groups, just encapsulated in the PowerShell -match operator. Returns the string for truthy, or empty string for falsey.

  • \$\begingroup\$ In the alternate version you should evaluate -match against $args[0], otherwise -match will work as a filter \$\endgroup\$ Jul 24, 2016 at 18:18
  • \$\begingroup\$ @MathiasR.Jessen In production code, yes, but we can golf the [0] here because we're only given one input and we only need one truthy/falsey value as output. Since an empty string is falsey and a non-empty string is truthy, we can filter against the array and either get the input string back or nothing back, which satisfies the challenge requirements. \$\endgroup\$ Jul 25, 2016 at 12:30

Pyth - 13 bytes



  qz          #is input equal to
          "ab #the string "ab"
     *        #multiplied by
      /lz2    #length of input / 2
    S         #and sorted?
&z            #(implicitly) print if the above is true and z is not empty
  • \$\begingroup\$ You can use a string as input and then make it &qS*/lQ2"ab \$\endgroup\$
    – Leaky Nun
    Jul 20, 2016 at 20:25
  • \$\begingroup\$ @LeakyNun thanks for the tip! Can you explain how/why that works? This is my first time ever using Pyth \$\endgroup\$ Jul 20, 2016 at 20:46
  • \$\begingroup\$ For example, +4 will expand to +4Q (implicit filling of arguments) \$\endgroup\$
    – Leaky Nun
    Jul 21, 2016 at 4:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy