# frozen_string_literal: true

# 839. Similar String Groups
# https://leetcode.com/problems/similar-string-groups
# Hard

=begin
Two strings X and Y are similar if we can swap two letters (in different positions) of X, so that it equals Y. Also two strings X and Y are similar if they are equal.

For example, "tars" and "rats" are similar (swapping at positions 0 and 2), and "rats" and "arts" are similar, but "star" is not similar to "tars", "rats", or "arts".

Together, these form two connected groups by similarity: {"tars", "rats", "arts"} and {"star"}.  Notice that "tars" and "arts" are in the same group even though they are not similar.  Formally, each group is such that a word is in the group if and only if it is similar to at least one other word in the group.

We are given a list strs of strings where every string in strs is an anagram of every other string in strs. How many groups are there?

Example 1:
Input: strs = ["tars","rats","arts","star"]
Output: 2

Example 2:
Input: strs = ["omv","ovm"]
Output: 1

Constraints:
1 <= strs.length <= 300
1 <= strs[i].length <= 300
strs[i] consists of lowercase letters only.
All words in strs have the same length and are anagrams of each other.
=end

# @param {String[]} strs
# @return {Integer}
def num_similar_groups(strs)
  strs.map! do |str|
    str.split("").map(&:ord)
  end

  @connected = strs.each_with_object({}) { |str, connected| connected[str] = [] }

  max_index = strs.size.pred
  strs.each_with_index do |str1, index1|
    index1.next.upto max_index do |index2|
      compare(str1, strs[index2])
    end
  end

  count_groups
end

def compare(str1, str2)
  counter = 0

  str1.each_with_index do |char1, char_index|
    if char1 != str2[char_index]
      counter += 1

      break if counter > 2
    end
  end

  if counter == 2
    @connected[str1] << str2
    @connected[str2] << str1
  end
end

def count_groups
  counter = 0

  until @connected.empty?
    counter += 1
    connected_strs = [ @connected.keys.first ]

    until connected_strs.empty?
      connected_strs.map! { |str| @connected.delete(str) }
      connected_strs.compact!
      connected_strs.flatten!(1)
    end
  end

  counter
end

# **************** #
#       TEST       #
# **************** #

require "test/unit"
class Test_num_similar_groups < Test::Unit::TestCase
  def test_
    assert_equal 2, num_similar_groups(["tars", "rats", "arts", "star"])
    assert_equal 1, num_similar_groups(["omv", "ovm"])
  end
end