As a point of reference, with most regex engines, Group matches don't accumulate like an array. Dot-Net is an exception which can do that (collections).
My apologies, you were right, it needs the alternation.
However, you have to force find the first OR c. This is done with a conditional lookahead.
Good luck!
# ^.*?(?:(?:(?<grp1>(?:\baa\b.*?\bbb\b|\bbb\b.*?\baa\b))(?(?=.*\b(?:cc|aa)\b).*(?<grp2>(?:\bcc\b|\baa\b))|))|(?<grp2>\b(?:cc|aa)\b))
^
.*?
(?:
(?: # Force find a AND b, OR c
(?<grp1>
(?:
\b aa \b .*? \b bb \b
| \b bb \b .*? \b aa \b
)
)
(?(?= # conditional assertion, force to find
.*
\b (?: cc | aa ) \b
)
.*
(?<grp2>
\b (?: cc | aa ) \b
)
|
)
)
|
(?<grp2> # Else, forcc find OR c
\b (?: cc | aa ) \b
)
)
Edit: This would match (aa cc), (bb)
But beware, the more permutations, the more complex. And that leads down the road to assertions, flags, condition's, all of which will slow performance and make maintaining somewhat tougher.
# ^.*?(?:(?:(?<grp1>(?:\baa\b(?:(?!cc).)*?\bbb\b|\baa\b(?:(?!bb).)*?\bcc\b|\bbb\b(?:(?!cc).)*?\baa\b|\bbb\b(?:(?!aa).)*?\bcc\b))(?(?=.*\b(?:aa|bb|cc)\b).*(?<grp2>\b(?:aa|bb|cc)\b)|))|(?<grp2>\b(?:cc|aa)\b))
^
.*?
(?:
# Force find: (aa bb), (cc)
# (aa cc), (bb)
# (bb aa), (cc)
# (bb cc), (aa)
(?:
(?<grp1> # GROUP1
(?:
\b aa \b (?:(?!cc).)*? \b bb \b
|
\b aa \b (?:(?!bb).)*? \b cc \b
|
\b bb \b (?:(?!cc).)*? \b aa \b
|
\b bb \b (?:(?!aa).)*? \b cc \b
)
)
(?(?= # Conditional assertion, find (aa), (bb), (cc)
.*
\b (?: aa | bb | cc ) \b
)
# The condition is true, so consume it
.*
(?<grp2> # GROUP2
\b (?: aa | bb | cc ) \b
)
| # The condition is false, match nothing
)
)
|
# Or,
# Force find: (), (aa)
# (), (bb)
# (), (cc)
(?<grp2> # GROUP2
\b (?: aa | bb | cc ) \b
)
)