g | x | w | all
Bytes Lang Time Link
093R240613T190159Zpajonk
125R240613T075103Zint 21h
120AWK231116T084836ZMarius_C
064AWK231226T094016ZBubbler
187Swift231223T153052ZAnnett S
065Gema231221T223315Zmanatwor
142TypeScript's Type System231107T130816Znoodle p
01405AB1E231115T095426ZKevin Cr
118Java JDK231110T135034ZFhuvi
057APL+WIN231107T183445ZGraham
164Java SE231108T164344ZTobias G
055Python231107T235050ZAlbert.L
083Python231107T140246Zmousetai
029Perl 5 p231108T140610ZNahuel F
241C# without Implicit global usings231109T085125Zgobes
3862Uiua231108T225831Zregr4444
012Nekomata231108T035415Zalephalp
093C#231108T161409ZAcer
023Retina 0.8.2231107T123410ZNeil
011Stax231108T032631Zjimmy230
014Jelly231107T193719ZNick Ken
041Ruby231108T124351ZG B
059JavaScript ES6231107T133402ZArnauld
027Uiua231108T103136ZMatthew
051Python231107T174554ZPhan Trọ
025J 9.4231108T004802ZBubbler
086Python231108T021038ZShadowRa
014Vyxal ≤ v2.21.12231108T003811Zemanresu
029Uiua231107T193802ZPseudo N
1325Vyxal231107T121059Zlyxal
nanJ231107T221117ZJonah
080Google Sheets231107T191614Zdoubleun
015Charcoal231107T124756ZNeil
017Jelly231107T173032ZJonathan
073Python 3231107T191814Zxnor
082Python 3.8231107T184049ZJonathan
064Ruby231107T155937ZKirill L
209Go231107T151054Zbigyihsu

R, 113 98 93 bytes

Edit: -15 and -5 bytes thanks to Dominic van Essen.

`?`=\(S,a=c("aa","ab","ba","bb","c"))`if`(length(a)>1,sub(a[!i],"",S)?a[i<-!pmatch(a,S,0)],a)

Attempt This Online!

Less sophisticated (straightforward) approach than @int 21h -- Glory to Ukraine --'s, but shorter. And using pmatch function that I learnt thanks to them!

R, 134 131 128 125 bytes

\(S,s=sub("c","",S),v=strtoi(s,16),u=c(170,171,186,187))`if`(S>s,{for(j in 1:3){u=u[u!=v%%256];v=v%/%256};as.hexmode(u)},"c")

Attempt This Online!

A semi-arithmetic approach: remove "c" (if any) read the rest as a hexadecimal value, get the remainder from division by 0x100, divide by 0x100, 3 times and output the missing number as a hexadecimal.

Missing "c" is treated separately.

AWK, 123 120 bytes

a["aa"];a["ab"];a["ba"];a["bb"];{if(!sub(/c/,z))$0="c";else{for(i=1;i<6;i+=2)delete a[substr($0,i,2)];for(k in a)$0=k}}1

Try it online!


Ungolfed :

a["aa"];a["ab"];a["ba"];a["bb"]
{
    if($0!~/c/)$0="c"
    else{
        sub(/c/,"",$0)
        for(i=1;i<6;i+=2) delete a[substr($0,i,2)]
        for(k in a)$0=k
    }
}
1

AWK, 67 64 bytes

patsplit($0,l,/c|[ab]./){$0="aaabbbbac";for(k in l)sub(l[k],a)}1

Attempt This Online!

Removed a semicolon by moving patsplit out to the pattern part (-1), and golfed the regex a bit (-2).

previous answer, 67 bytes:

{patsplit($0,l,/c|[ab]{2}/);$0="aaabbbbac";for(k in l)sub(l[k],a)}1

Attempt This Online!

Replacing without splitting "aaabbbbac" into chunks first works because

Swift, 187 bytes

Short version:

func m(_ s: String)->String{var t:Set=["aa","ab","ba","bb","c"];let s=s.map({String($0)});var i=0;while i<s.count{var x=s[i];if x != "c"{i+=1;x+=s[i];};t.remove(x);i+=1};return t.first!}

Verbose version:

func m2(_ s: String) -> String {
    var t:Set = ["aa","ab","ba","bb","c"]
    let s = s.map({String($0)})
    var i = 0
    while i < s.count {
        var x = s[i]
        if x != "c" {
            i += 1
            x += s[i]
        }
        t.remove(x)
        i += 1
    }
    return t.first!
}

Gema, 96 65 characters

c=@set{c;}
??=@set{??;}
\Z=${aa;aa}${ab;ab}${ba;ba}${bb;bb}${c;c}

Sample run:

bash-5.2$ echo -n 'aaabbac' | gema 'c=@set{c;};??=@set{??;};\Z=${aa;aa}${ab;ab}${ba;ba}${bb;bb}${c;c}'
bb

Try it online! / Try all test case online!

TypeScript's Type System, 144 142 bytes

type F<S,X=0>=S extends`c${infer R}`?F<R,X|"c">:S extends`${infer A}${infer B}${infer R}`?F<R,X|`${A}${B}`>:Exclude<"aa"|"ab"|"ba"|"bb"|"c",X>

Try it at the TypeScript playground.

This is only so long because it has to check for "c" separately because TypeScript annoyingly doesn't let you infer part of a string that is a union of strings of variable length. I also have a different idea that might be shorter, but it's not at all obvious how to accomplish it in TS types so it might take me a while to figure out.

05AB1E, 14 bytes

'cK2ôH.«^12Mhl

Port of @Bubbler's J & Vyxal answer, so make sure to upvote that answer as well!

Try it online or verify all possible I/O test cases.

Explanation:

'cK            '# Remove a potential "c" from the (implicit) input-string
   2ô           # Split it into pairs
     H          # Convert each string-pair from hexadecimal to a base-10 integer
      .«        # Reduce this list by:
        ^       #  Bitwise-XORing
         12     # Push 12
           M    # Push the largest value on the stack
            h   # Convert it from a base-10 integer to (uppercase) hexadecimal
             l  # Convert it to lowercase
                # (after which the result is output implicitly)

Java (JDK), 118 bytes

Adaptation of Arnauld's first JS answer

(this could probably be outgolfed with the new base-16 trick)

s->{var r="";for(var e:("aaabbabbc"+s).split("(?<=\\G..|c)"))r=r.contains(e)?r.replace(e,""):r+e+" ";return r.trim();}

Try it online!


We concatenate the string of all possible values with the string parameter, so that the whole resulting string contains every value twice, except for the "missing" value which will only appear once.

Then we use a regex to split the whole string into either pairs of characters, or the character "c" alone.

We parse the resulting array, and in a string accumulator we add any unseen value (with a space separator) and remove every value already seen. At the end, the accumulator contains only the missing value, and lots of spaces that we trim.

APL+WIN, 69, 63, 57 bytes.

A bit more golfing saves another 6 bytes.

Prompts for string:

((8=⍴i)/'c'),(~s∊⊂[2]((⍴v),2)⍴v←(i←⎕)~'c')/s←,'ab'∘.,'ab'

Try it online! Thanks to Dyalog Classic

Java (SE), 164 bytes

b->{if(b.contains("c"))l:for(var c:"aa ab ba bb".split(" ")){for(int i=0;i<6;i+=2)if(c.contains(b.replace("c","").substring(i,i+2)))continue l;return c;}return"c";}

Try it online!

Formatted:

    static Function<String, String> f1 = b -> {
        if (b.contains("c"))
            l:for (var c : "aa ab ba bb".split(" ")) {
                for (int i = 0; i < 6; i += 2)
                    if (c.contains(b.replace("c", "").substring(i, i + 2)))
                        continue l;
                return c;
            }
        return "c";
    };

Logs:

Python, 55 bytes

lambda a:f'{int("cc"+(30*a).strip("ab"),16)%255%192:x}'

Attempt This Online!

This produces 15 (= base - 1) on frame and (if c is present) 15 off frame copies of the input. The sum will therefore be 0 modulo 0x11 x 0x0f. .strip("ab") removes one set of on frame copies of all but c. Adding 0xcc which is the sum of the four possible non c terms mod 0xff yields the missing item unless it is c which is dealt with separately.

Python, 57 bytes

lambda s:" aabbcbaba"[int(s.replace("c",""),14)%195%8::5]

Attempt This Online!

How?

First get rid of the evil frame-shifting "c". Afterwards read as base 14 and add all pairs by reducing modulo 195. Further reduce and produce the answer by indexing into a magic string.

Python, 83 bytes

lambda a:min({'aa','ab','bb','ba','c'}-{a.replace('c','')[i:i+2]for i in[0,2,4,6]})

Attempt This Online!

Perl 5 (-p), 32, 29 bytes

$_=s/c//?eval s/..\K\B/^/gr:c

Try it online!

If c can't be removed returns c otherwise bitwise xor 2 char strings.

-3 bytes thanks to Kjetil S, changing (?!$) (negative lookahead end anchor) with \B (non-word-boundary)

C# (without Implicit global usings), 241 bytes

var v=new System.Collections.Generic.List<string>();int i=0;while(v.Count<4){if(args[0][i]=='c')v.Add("c");else{v.Add(args[0].Substring(i,2));i++;}i++;}foreach(var k in"aa|ab|ba|bb|c".Split('|'))if(!v.Contains(k))System.Console.WriteLine(k);

This is my first golf, and the language isn't very golfable... be indulgent :-)

I didn't find a online compiler which handle input arguments correctly ; so to test this, paste it in a Visual Studio .cs file.

Uiua, 38 characters (62 bytes)

("c";|⊢▽¬∊,⊃(↯¯1_2▽∊,)(/⊂⊞⊂.)"ab")∊@c.

Uiuapad

("c";|⊢▽¬∊,⊃(↯¯1_2▽∊,)(/⊂⊞⊂.)"ab")∊@c.­⁡​‎‎⁡⁠⁡‏⁠‎⁡⁠⁢⁢‏⁠‎⁡⁠⁣⁡⁢‏⁠‎⁡⁠⁣⁡⁣‏⁠‎⁡⁠⁣⁡⁤‏⁠‎⁡⁠⁣⁢⁡‏⁠‎⁡⁠⁣⁢⁢‏‏​⁡⁠⁡‌⁢​‎‎⁡⁠⁢‏⁠‎⁡⁠⁣‏⁠‎⁡⁠⁤‏⁠‎⁡⁠⁢⁡‏‏​⁡⁠⁡‌⁣​‎‎⁡⁠⁣⁤‏⁠‎⁡⁠⁢⁢⁣‏⁠‎⁡⁠⁢⁢⁤‏⁠‎⁡⁠⁢⁣⁡‏⁠‎⁡⁠⁢⁣⁢‏⁠‎⁡⁠⁢⁣⁣‏⁠‎⁡⁠⁢⁣⁤‏⁠‎⁡⁠⁢⁤⁡‏⁠‎⁡⁠⁢⁤⁢‏⁠‎⁡⁠⁢⁤⁣‏⁠‎⁡⁠⁢⁤⁤‏⁠‎⁡⁠⁣⁡⁡‏‏​⁡⁠⁡‌⁤​‎‎⁡⁠⁣⁤‏⁠‎⁡⁠⁤⁡‏⁠‎⁡⁠⁤⁢‏⁠‎⁡⁠⁤⁣‏⁠‎⁡⁠⁤⁤‏⁠‎⁡⁠⁢⁡⁡‏⁠‎⁡⁠⁢⁡⁢‏⁠‎⁡⁠⁢⁡⁣‏⁠‎⁡⁠⁢⁡⁤‏⁠‎⁡⁠⁢⁢⁡‏⁠‎⁡⁠⁢⁢⁢‏‏​⁡⁠⁡‌⁢⁡​‎‎⁡⁠⁢⁣‏⁠‎⁡⁠⁢⁤‏⁠‎⁡⁠⁣⁡‏⁠‎⁡⁠⁣⁢‏⁠‎⁡⁠⁣⁣‏‏​⁡⁠⁡‌­
(    |                              )∊@c.  # ‎⁡Is 'c' a member of the string?
 "c";                                      # ‎⁢If not, just return 'c'
            ⊃            (/⊂⊞⊂.)"ab"       # ‎⁣Else, generate the list L = ["aa" "ab" "ba" "bb"]
            ⊃(↯¯1_2▽∊,)                   # ‎⁤And filter out any the c's from the string (by keeping only members of "ab") and reshape it into a n×2 matrix
      ⊢▽¬∊,                               # ‎⁢⁡Return the element of L which does not appear in this matrix.

Nekomata, 12 bytes

ĕ<ĭÐŤʳXH"c"I

Attempt This Online!

The xor trick is borrowed from this answer by Bubbler.

ĕ<ĭÐŤʳXH"c"I
ĕ                   Pick one char out from the input string
 <                  Check that all remaining chars are less than the picked char
                    If this does not fail, which means the input contains 'c':
  ĭÐ                    Uninterleave the remaining chars into a pair of strings
    Ť                   Transpose
     ʳX                 Reduce by xor (chars are converted to code points implicitly)
       H                Convert from code points
           I        Otherwise:
        "c"             Return "c"

C#, 93 bytes

x=>"aa ab ba bb c".Split().Except(x.Replace("c","").Chunk(2).Select(z=>""+z[0]+z[1])).First()

Explanation

x =>                         // input string
   "aa ab ba bb c".Split()   // construct array with the four pairs + c
   .Except(                  // remove from it...
       x.Replace("c","")     // remove c from input
       .Chunk(2)             // split into chunks of size 2 (each chunk is a char[])
       .Select(              // transform each item
           z=>""+z[0]+z[1]   // convert char[] to string
       )
   ).First();                // first item remaining. this is c if all pairs were removed

Try it online!

Uses .Chunk which isn't supported on tio.run.

Retina 0.8.2, 29 23 bytes

$
¶aaabbbbac
D`c|..
1A`

Try it online! Link includes test cases. Explanation:

$
¶aaabbbbac

Append another copy of the whole list on a separate line.

D`c|..

Remove duplicate elements from the copy.

1A`

Remove the original input.

Stax, 11 bytes

ù¢╓┘0ï<ΣEZ1

Run and debug it

Unpacked (13 bytes):

'c|^2/M{{SkmT

Explanations:

'c|^         Set xor with "c" without deduplication or changing of order.
2/           Split into 2 character chunks.
M            Transpose (aka zip).
{{Skm        Map by reduce by xor of the character codes.
             The two lines work like reducing by vectorized xor.
T            Trim on the right. For some reasons null bytes are also
             printed as spaces and are also removed.

The difficult part is to find the right language that had all the required features. Many languages have them but they may not be the default behavior of the most obvious operator, so I thought they don't. The most important one is set xor (set symmetric difference) without deduplication. Others are splitting to equally sized chunks, and doing mathematics on character values. But Stax doesn't have map and reduce operators with block starters omitted for a single character operation.

Vyxal, 87 bitsv2, 11 10.875 bytes

\cÞ⊍C2ẇƒ꘍C∑Ǎ

Try it Online!

Bitstring:

011010100001110110101010000101011001101011111101101001000110111010111001101110101010000

Explanations:

\cÞ⊍         Set xor with "c" without deduplication or changing of order.
C            Convert characters to numbers.
2ẇ           Split into 2 character chunks.
ƒ꘍           Reduce by automatically vectorized xor.
C            Convert back to characters.
∑            Concatenate.
Ǎ            Remove non-letters, i.e. the null byte if the answer is "c".

Jelly, 15 14 bytes

ḟ”cQp`ḟs2$Ʋȯ”c

Try it online!

Thanks to @JonathanAllan for saving a byte!

A full program taking a string as its argument and printing the missing piece. (It can also be used as a monadic link, but will return a Jelly string if the result is one of the ab permutations and a single character for c.)

Explanation

ḟ”cQp`ḟs2$Ʋȯ”c
ḟ”c            | Filter out "c"
         Ʋ     | Following as a monad:
  Q            | - Uniquify
   p`          | - Cartesian product with itself
     ḟ         | - Filter out
      s2$      |   - The c-less input split into twos
           ȯ”c | Or "c"

Alternative approach: Jelly, 10 bytes

œ^”cs2œ^"/

Try it online!

This is a translation of @jimmy23013’s clever Stax answer; I’ve not replaced my main one since I wanted to preserve my different approach, but this is clearly shorter!

œ^”cŒœœ^/€ would also work for ten.

Ruby, 41 bytes

->s{"%x"%(726-s.scan(/c|../).sum(&:hex))}

Try it online!

0xaa+0xab+0xba+0xbb+0xc is 726.

JavaScript (ES6), 59 bytes

s=>(s+"aaabbabbc").replace(q=/c|../g,s=>(q[s]^=1)?o=s:0)&&o

Try it online!

Commented

s =>              // s = input string
(s + "aaabbabbc") // append all the patterns to s
.replace(         // replace:
  q =             //   we use q as an object to keep track of
                  //   encountered patterns
  /c|../g,        //   a pattern is either 'c' or 2 characters
  s =>            //   for each matched pattern s:
    (q[s] ^= 1) ? //     if this is the first time we see it:
      o = s       //       save it into o
    :             //     else:
      0           //       do nothing
) && o            // end of replace(); return o (i.e. the last
                  // pattern seen for the first time)

JavaScript (ES6), 52 bytes

Porting Phan Trọng Nhân's excellent answer is definitely shorter.

s=>(216-`0x${s}`.replace('c','0c')%255).toString(16)

Try it online!


JavaScript (ES6), 64 bytes

My original answer.

s=>"b__c__aab"[q=s.replace(/c|../g,s=>'0x'+s|0)%9]+["ab"[q%7%5]]

Try it online!

How?

Identifying the missing pattern

We look for the relevant patterns in the input string with /c|../g and replace each of them with its decimal value when parsed as hexadecimal.

This gives:

original pattern replaced with
aa 170
ab 171
ba 186
bb 187
c 12

For instance, "abbabbc" is turned into "17118618712".

It is a well known fact that \$n \bmod 9\$ is the sum of the decimal digits of \$n\$ modulo \$9\$. (Obviously, the order of the digits doesn't matter.)

Because each decimal pattern described above has a different remainder modulo \$9\$, we can identify the missing one by just reducing the entire transformed string modulo \$9\$:

\$q=n \bmod 9\$ missing pattern
0 ba
3 c
6 ab
7 aa
8 bb

Output

We use a 9-character lookup string for the first character:

"b__c__aab"[q=...]

We could also use the following trick, which is unfortunately just as long:

"bac"[7/(q=...)|0]

And we use a shorter lookup string with a modulo chain for the 2nd character, wrapped into brackets so that undefined is turned into an empty string for \$q=3\$:

["ab"[q%7%5]]

Uiua, 27 bytes

?"c"⍜(-@a)(◿2/+↯~2▽<2.)=8⧻.

Try it online!

Explanation:

For the cases where 'c' is in the string, this uses a little trick by converting 'a' to 0 and 'b' to 1, then the XOR of the three pairs results in the missing pair.

?"c"                    =8⧻.   # If the string length is 8, return "c"
    ⍜(-@a)(           )       # Subtract char 'a' from the string to get an
                               # array where a=0, b=1, c=2. Then perform the
                               # following and add char 'a' back at the end:
                   ▽<2.        # Remove any 2s (char 'c')
                ↯~2            # Reshape to pairs
            ◿2/+               # XOR by adding the pairs and taking modulo 2

Example:

Input: "cbbbaab"

Operation Stack (top on left):
"cbbbaab"
⧻. 7 "cbbbaab"
=8 0 "cbbbaab"
?"c" "cbbbaab"
⍜(-@a)( [2 1 1 1 0 0 1]
▽<2. [1 1 1 0 0 1]
↯~2 [[1 1] [1 0] [0 1]]
/+ [2 2]
◿2 [0 0]
) "aa"

Python,  55  51 bytes

lambda a:'%x'%(-int(a.replace('c','0c'),16)%255-39)

Attempt This Online!

Explain:

Logs:

Python,  94 92 bytes

lambda a:('c'in a)*''.join(chr(390-sum(map(ord,a.replace('c','')[i::2])))for i in(0,1))or'c'

Attempt This Online!

Logs:

J 9.4, 25 bytes

(]>.&XOR&.dfh _2]\-.)&'c'

Attempt This Online!

dfh and hfd were recently modified to have each other as inverses. ATO doesn't have the change yet.

J, 26 bytes

12(hfd@>.XOR)_2 dfh\-.&'c'

Attempt This Online!

The XOR of three of aa, ab, ba, bb interpreted as hexadecimal (or any base that is a greater power of 2) is the missing one, and the XOR of all four is zero. When all four are present, the XOR value is replaced with 12 before converting back to hexadecimal.

12(hfd@>.XOR)_2 dfh\-.&'c'    input: a string
                    -.&'c'    remove 'c'
             _2 dfh\          convert each chunk of 2 with "hex->int"
  (      XOR)                 reduce by bitwise XOR
12     >.                     max with 12; if the result is 0, use 12 instead
   hfd@                       convert with "int->hex"

Vyxal, 99 bitsv2, 12.375 bytes

\co2ẇHƒ꘍12∴k6τ

Try it Online!

Bitstring:

011010100001111110010011010110100010110000110101011111111011101001101110100010100100100101011011010

Literal translation of the J solution above.

\co2ẇHƒ꘍12∴k6τ
\co               remove 'c'
   2ẇ             chunks of 2
     H            convert each with "hex->int"
      ƒ꘍          reduce by bitwise XOR
        12∴      max with 12
            k6τ   into base "lower hex digits"; convert with "int->hex"

Python, 86 bytes

lambda s:[*{'aa','ab','ba','bb'}-{*map(str.__add__,*[filter('c'.__ne__,s)]*2)},'c'][0]

Attempt This Online!

No magic numbers here, just another approach to solving in Python, this one a straightforward-ish golfing of removing each of the pairs in the string (with 'c' removed) from the initial set, and if none are left, defaulting to 'c'.

Vyxal v2.21.12, 14 bytes

\cẆǒf«H₴Ǐ3F«ǒ⊍

Try it Online! (code modified to work on current version)

I'm using an older version of Vyxal because for some reason ǒ was modified in a recent version. Also, yes there's another Vyxal answer, but this one's shorter in regular SBCS bytes.

  Ẇ            # Split (keeping delimiter) on
\c             # "c"
   ǒf          # Cut each into chunks of length 2
     «H₴Ǐ3F«   # Compressed string "abaabbbac"
            ǒ  # Cut into chunks of length 2
             ⊍ # Take the set difference

Uiua, 31 29 bytes

Two bytes saved by replacing "ab" with ⊝.

⊔⊡⊗0⊐⊃∊'⊂⊙"c"∩(↯~2)⊞⊟.⊝.▽≠@c.

Try it online!

Interestingly, since Uiua has an autoformatter that runs during execution, afterwards it is 30 bytes. (Specifically, '⊂⊙"c" is turned into (⊂⊙"c"))

Explanation

                          ▽≠@c. # Remove all c from the string
                     ⊞⊟.⊝.      # construct "ab" and generate all pairs of its elements
               ∩(↯~2)           # Simultaneously:
                                  # flatten the table into an array of 2-char strings
                                  # chunk the input (sans c) into 2-char strings
     ⊐⊃∊                        # Both find which of aa,ab,ba,bb are in chunked input...
        '⊂⊙"c"                  # ...and add c to the array of aa,ab,ba,bb
  ⊗0                            # Index of pair that was not found (4 if all were found)
⊔⊡                              # Select from aa,ab,ba,bb,c

Vyxal, 106 bitsv2, 13.25 bytes

‛ab2↔\cpṖ'ḣ∑?=;hh

Try it Online!

Bitstring:

1010100100110111101010010101000011000010000011011100110001101010100111101011100111001010011100110100101110

Explained

«F^ǒ⇩v«2ẇṖ'ḣ∑?=;hh­⁡​‎‎⁡⁠⁣⁢‏‏​⁡⁠⁡‌⁢​‎‎⁡⁠⁡‏⁠‎⁡⁠⁢‏⁠‎⁡⁠⁣‏⁠‎⁡⁠⁤‏⁠‎⁡⁠⁢⁡‏⁠‎⁡⁠⁢⁢‏⁠‎⁡⁠⁢⁣‏‏​⁡⁠⁡‌⁣​‎‎⁡⁠⁢⁤‏⁠‎⁡⁠⁣⁡‏‏​⁡⁠⁡‌⁤​‎‎⁡⁠⁣⁣‏⁠‎⁡⁠⁤⁤‏‏​⁡⁠⁡‌⁢⁡​‎‎⁡⁠⁣⁤‏‏​⁡⁠⁡‌⁢⁢​‎‎⁡⁠⁤⁡‏‏​⁡⁠⁡‌⁢⁣​‎‎⁡⁠⁤⁢‏⁠‎⁡⁠⁤⁣‏‏​⁡⁠⁡‌⁢⁤​‎‎⁡⁠⁢⁡⁡‏⁠‎⁡⁠⁢⁡⁢‏‏​⁡⁠⁡‌­
         Ṗ          # ‎⁡Permutations of
«F^ǒ⇩v«             # ‎⁢The string "aaabbabbc"
       2ẇ           # ‎⁣split into chunks of size 2
          '    ;    # ‎⁤Filtered by:
           ḣ        # ‎⁢⁡  the permutation with the head removed
            ∑       # ‎⁢⁢  joined into a single string
             ?=     # ‎⁢⁣  equals the input.
                hh  # ‎⁢⁤Get the first item of the first item in that list.
💎

Created with the help of Luminespire.

J, 31 30 29 bytes

(e.~{];,@{@;~@-.-._2<\-.)&'c'

Try it online!

-1 thanks to ovs

J, 30 bytes

(-.~,&;(,{;~'ab')-._2<\-.)&'c'

Try it online!

A similar alternate solution that has unboxed output and no conditional logic. Feels slightly cleaner to me despite being 1 byte longer.

Google Sheets, 80 bytes

=substitute(regexreplace("aa|ab|ba|bb|c",regexreplace(A1,"(c|..)","$1|"),),"|",)

Put the input in cell A1 and the formula in B1.

Nothing clever here, just two replaces, plus removal of hardcoded separators.

Translation to JavaScript (90 bytes, non-competing):

s=>(r=(x,y,z)=>x.replace(RegExp(y,'g'),z||''))(r('aa#ab#ba#bb#c',r(s,'(c|..)','$1|')),'#')

Charcoal, 19 15 bytes

⌊⁻⪪”&∨O⊕”²⪪⁻Sc²

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

   ”...”        Compressed string `aaabbbbac`
  ⪪     ²       Split into pairs of characters
 ⁻              Remove elements from
           S    Input string
          ⁻ c   Remove the `c`
         ⪪   ²  Split into pairs of characters
⌊               Take the minimum
                Implicitly print

If all of the a/b pairs were successfully removed then the only remaining element is the trailing c otherwise the minimum element is the missing a/b pair.

Jelly,  20 18  17 bytes

-3 thanks to Nick Kennedy reminding me to golf the magic table.

⁾abp`”cṭŒ!F=¥Þ⁸ṪṪ

A full program that accepts the flattened, redacted permutation and prints the missing item.

Try it online! Or see all twenty four of each of the five choices here.

How?

Brute force...

⁾abp`”cṭŒ!F=¥Þ⁸ṪṪ - Link: list of characters, X
⁾ab               - "ab"
   p`             - Cartesian product with itself -> ["aa","ab","ba","bb"]
     ”cṭ          - tack 'c' character -> ["ab","ba","bb","aa",'c']
        Œ!        - all permutations
             Þ    - sort by:
            ¥ ⁸   -   last two links as a dyad - f(Permutation, X):
          F       -     flatten the Permutation
           =      -     equals X (vectorised)
               Ṫ  - tail -> permutation with matching prefix
                Ṫ - tail -> the missing pair
                  - implicit print

Non-brute-force 21 bytes

I quite like this non-brute force method at 21 bytes though (maybe it can be improved upon?)...

ḟ”cOḤÐoS;Ḥ$%5Qị“bbaac

A full program that accepts the flattened, redacted permutation and prints the missing item.

Try it online! Or see all twenty four of each of the five choices here.

How?

ḟ”cOḤÐoS;Ḥ$%5Qị“bbaac - Main Link: list of characters
ḟ”c                   - remove any 'c' character
   O                  - cast to ordinal values ('a' -> 97, 'b' -> 98)
     Ðo               - apply to odd indices (first, third, ...):
    Ḥ                 -   double
       S              - sum
        ;Ḥ$           - concatenate double this value -> [sum, 2×sum]
           %5         - modulo five -> [sum%5, (2×sum)%5]
             Q        - deduplicate
              ị“bbaac - index into "bbaac"

Examples:

1. missing "c" "aaabbbba"

-> "aaabbbba" (removed 'c'; no change)
-> [97,97,97,98,98,98,98,97] (cast to ordinals)
-> [194,97,194,98,196,98,194,97] (double values at odd indices)
-> 1170 (sum those)
-> [1170, 2340] (concatenate double itself)
-> [0, 0] (modulo five)
-> [0] (deduplicate)
-> "c" (index into "bbaac" - Note that indexing is one-based and modular)

2. missing "bb" "aaacbba"

-> "aaabba" (removed 'c')
-> [97,97,97,98,98,97] (cast to ordinals)
-> [194,97,194,98,196,97] (double values at odd indices)
-> 876 (sum those - i.e. \$1170 - (2 \times 98) - 98 = 876\$)
-> [876, 1752] (concatenate double itself)
-> [1, 2] (modulo five)
-> [1, 2] (deduplicate; no change)
-> "bb" (index into "bbaac")

Python 3, 73 bytes

b,*l=0,{'c'},{0}
for c in input():l[b]^={c};b^=c<'c'
print(str(l)[3::10])

Try it online!

71 bytes

b=k=62
for c in input():k^=ord(c)*b;b^=c<'c'
print("babbaac"[k%7:][:2])

Try it online!

Python 3.8, 82 bytes

lambda a:("cbbaa"*469)[(k:=sum(map(ord,(r:=a.replace('c',''))+r[::2])))::k][r==a:]

An unnamed function that accepts the flattened, redacted permutation and returns the missing item.

Try it online!

A port of my non-brute-force Jelly solution.

Ruby, 64 bytes

->s{a=%w[aa ab ba bb c];a.find{[*(a-[_1]).permutation].join[s]}}

Attempt This Online!

Go, 209 bytes

import."strings"
func f(s string)string{r,S:=ReplaceAll(s,"c",""),map[string]int{"aa":0,"ab":0,"ba":0,"bb":0}
for i:=0;i<len(r);i+=2{k:=r[i:i+2]
if S[k]<1{S[k]++}}
for k,v:=range S{if v<1{return k}}
return"c"}

Attempt This Online!

Explanation

import."strings"
func f(s string)string{
// remove "c" from the input. guaranteed to be even length
r:=ReplaceAll(s,"c","")
// map to keep track of seen pairs
S:=map[string]int{"aa":0,"ab":0,"ba":0,"bb":0}
// for each pair of chars...
for i:=0;i<len(r);i+=2{
// get the pair
k:=r[i:i+2]
// if pair is not seen yet, increment the counter
if S[k]<1{S[k]++}}
// for each pair...
for k,v:=range S{
// if not seen, return that pair
if v<1{return k}}
// otherwise return "c"
return"c"}