If (a) preserving the order of the minuend list and (b) respecting the order of the subtrahend list are important to you (or to someone else reading this post), here's a neat solution that seems to perform well:
listComplement1[a_List, b_List] :=
Module[{j = 1},
With[{bn = Length@b},
Select[a, j > bn || # =!= b[[j]] || ++j &]
]
]
listComplement1[a, b]
(* Out: {"C", "A", "A", "A", "B", "C"} *)
And just because I can, here's a semi-compiled version that uses integer codes for distinct elements! Wheee!
listComplement2Helper =
Compile[{{a, _Integer, 1}, {b, _Integer, 1}},
Module[{j = 1},
With[{bn = Length@b},
Select[a,
If[j > bn || # =!= b[[j]],
True,
++j; False
] &
]
]
]
];
listComplement2[a_List, b_List] :=
With[{rels = MapIndexed[# -> #2[[1]] &, DeleteDuplicates@a]},
With[
{fwdMap = Dispatch@Append[rels, _ -> 0],
revMap = Dispatch[Reverse /@ rels]},
Replace[
listComplement2Helper[
Replace[a, fwdMap, {1}],
Replace[b, fwdMap, {1}]],
revMap,
{1}
]
]
]
listComplement2[a, b]
(* Out: {"C", "A", "A", "A", "B", "C"} *)
At first I thought that integer codes could speed things up when the list elements are (a) very many, (b) very complicated, and (c) very similar, but now I'm thinking it won't help much (if at all) due to the comparisons that have to happen anyway for DeleteDuplicates... Oh well, still fun.
Caveat: All elements in b after the first element in b that is not also in a will not be removed from a.
Note: This answer previously also suggested a solution using SequenceAlignment. Unfortunately, this approach doesn't seem to work. I couldn't figure out a nice way to adapt SequenceAlignment to the requirements here.