<p><strong>Attention</strong>: In this version, the number of operations that can be performed, has been increased to <strong>twice</strong>.<!-- notionvc: 278e7cb2-3b05-42fa-8ae9-65f5fd6f7585 --></p>

<p>You are given an array <code>nums</code> consisting of positive integers.</p>

<p>We call two integers <code>x</code> and <code>y</code> <strong>almost equal</strong> if both integers can become equal after performing the following operation <strong>at most <u>twice</u></strong>:</p>

<ul>
	<li>Choose <strong>either</strong> <code>x</code> or <code>y</code> and swap any two digits within the chosen number.</li>
</ul>

<p>Return the number of indices <code>i</code> and <code>j</code> in <code>nums</code> where <code>i &lt; j</code> such that <code>nums[i]</code> and <code>nums[j]</code> are <strong>almost equal</strong>.</p>

<p><strong>Note</strong> that it is allowed for an integer to have leading zeros after performing an operation.</p>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>

<div class="example-block">
<p><strong>Input:</strong> <span class="example-io">nums = [1023,2310,2130,213]</span></p>

<p><strong>Output:</strong> <span class="example-io">4</span></p>

<p><strong>Explanation:</strong></p>

<p>The almost equal pairs of elements are:</p>

<ul>
	<li>1023 and 2310. By swapping the digits 1 and 2, and then the digits 0 and 3 in 1023, you get 2310.</li>
	<li>1023 and 213. By swapping the digits 1 and 0, and then the digits 1 and 2 in 1023, you get 0213, which is 213.</li>
	<li>2310 and 213. By swapping the digits 2 and 0, and then the digits 3 and 2 in 2310, you get 0213, which is 213.</li>
	<li>2310 and 2130. By swapping the digits 3 and 1 in 2310, you get 2130.</li>
</ul>
</div>

<p><strong class="example">Example 2:</strong></p>

<div class="example-block">
<p><strong>Input:</strong> <span class="example-io">nums = [1,10,100]</span></p>

<p><strong>Output:</strong> <span class="example-io">3</span></p>

<p><strong>Explanation:</strong></p>

<p>The almost equal pairs of elements are:</p>

<ul>
	<li>1 and 10. By swapping the digits 1 and 0 in 10, you get 01 which is 1.</li>
	<li>1 and 100. By swapping the second 0 with the digit 1 in 100, you get 001, which is 1.</li>
	<li>10 and 100. By swapping the first 0 with the digit 1 in 100, you get 010, which is 10.</li>
</ul>
</div>

<p>&nbsp;</p>
<p><strong>Constraints:</strong></p>

<ul>
	<li><code>2 &lt;= nums.length &lt;= 5000</code></li>
	<li><code>1 &lt;= nums[i] &lt; 10<sup>7</sup></code></li>
</ul>

class Solution:
    def countPairs(self, nums: List[int]) -> int:
        n = len(nums)
        nums_str = list(map(str, nums))
        max_len = max(map(len, nums_str))
        for i in range(n):
            nums_str[i] = "0" * (max_len - len(nums_str[i])) + nums_str[i]

        v_freq = defaultdict(int)
        variant_dict = {}
        for s in nums_str:
            variants = variant_dict.setdefault(s, set())
            if not variants:
                variants.add(s)
                builder = list(s)
                for j in range(len(builder)):
                    for k in range(j + 1, len(builder)):
                        builder[j], builder[k] = builder[k], builder[j]
                        variants.add("".join(builder))
                        for l in range(len(builder)):
                            for m in range(l + 1, len(builder)):
                                builder[l], builder[m] = builder[m], builder[l]
                                variants.add("".join(builder))
                                builder[l], builder[m] = builder[m], builder[l]
                        builder[j], builder[k] = builder[k], builder[j]
            #print(s, variants)
            for v in variants:
                v_freq[v] += 1
        
        ans = 0

        for s in nums_str:
            ans += v_freq[s] - 1

        return ans // 2

    
    def countPairs_set(self, nums: List[int]) -> int:
        num_components = defaultdict(list)
        n = len(nums)
        nums_str = list(map(str, nums))
        max_len = max(map(len, nums_str))
        for i in range(n):
            nums_str[i] = "0" * (max_len - len(nums_str[i])) + nums_str[i]
    
        ans = 0
        variants = [set() for _ in range(n)]
        for i in range(n):
            digit_count = [0] * 10
            for c in nums_str[i]:
                digit_count[ord(c) - ord('0')] += 1
            key = tuple(digit_count)
            num_components[key].append(i)

            variants[i].add(nums_str[i])
            builder = list(nums_str[i])
            for j in range(len(builder)):
                for k in range(j + 1, len(builder)):
                    builder[j], builder[k] = builder[k], builder[j]
                    variants[i].add("".join(builder))
                    builder[j], builder[k] = builder[k], builder[j]

        for k, v in num_components.items():
            if len(v) <= 1:
                continue
            
            for i, j in combinations(v, 2):
                if len(variants[i] & variants[j]) >= 1:
                    ans += 1
        return ans



    def countPairs_BF(self, nums: List[int]) -> int:
        num_components = defaultdict(list)
        n = len(nums)
        nums_str = list(map(str, nums))
        max_len = max(map(len, nums_str))
        for i in range(n):
            nums_str[i] = "0" * (max_len - len(nums_str[i])) + nums_str[i]
    
        ans = 0
        for i in range(n):
            digit_count = [0] * 10
            for c in nums_str[i]:
                digit_count[ord(c) - ord('0')] += 1
            key = tuple(digit_count)
            num_components[key].append(i)

        for k, v in num_components.items():
            if len(v) <= 1:
                continue
            
            for i, j in combinations(v, 2):
                misaligned_nums = []
                for c1, c2 in zip(nums_str[i], nums_str[j]):
                    if c1 != c2:
                        misaligned_nums.append((c1,c2))
                if len(misaligned_nums) <= 3:
                    ans += 1
                elif len(misaligned_nums) == 4:
                    e = misaligned_nums.pop()
                    try:
                        misaligned_nums.remove((e[1], e[0]))
                    except ValueError:
                        continue
                    e = misaligned_nums.pop()
                    try:
                        misaligned_nums.remove((e[1], e[0]))
                    except ValueError:
                        continue
                    ans += 1

                    
        return ans

80026