let canonical s =
  let chars = Array.make 26 0 in
  for i = 0 to String.length s - 1 do
    let k = Char.code s.[i] - Char.code 'a' in
    if k >= 0 && k < 26 then chars.(k) <- chars.(k) + 1
  done;
  let buf = Buffer.create 26 in
  for i = 0 to 25 do
    for _ = 1 to chars.(i) do
      Buffer.add_string buf (String.make 1 (Char.chr (i + Char.code 'a')))
    done
  done;
  Buffer.contents buf

let group_anagrams xs =
  let h = Hashtbl.create 8 in
  List.iter (fun s ->
    let k = canonical s in
    let cur = match Hashtbl.find_opt h k with
              | Some xs -> xs
              | None -> []
    in
    Hashtbl.replace h k (s :: cur)
  ) xs;
  Hashtbl.length h

;;

group_anagrams ["eat"; "tea"; "tan"; "ate"; "nat"; "bat"]
