<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:

    # SHORTCUT: instead of taking each value, performing every possible
    #   character swap on it, and then doing that *again*, and comparing
    #   all those values against the other values:
    # we instead perform every possible swap on each value *once*,
    #   store them as a set, and check the intersection of each pair of sets.

    # we borrow some code from #3265:

    def join(self, L: List[str]) -> str:
        # print(f'join({L})')
        return ''.join(L)

    def swapped(self, L: List[str], i: int, j: int) -> List[str]:
        assert i < j
        preI = L[:i]        # everything before index i
        I = tuple(L[i])     # item at index i
        ItoJ = L[i+1:j]     # everything after index i and before index j
        J = tuple(L[j])     # item at index j
        postJ = L[j+1:]     # everything after index j

        S = (preI + J + ItoJ + I + postJ)
        
        return tuple(S)

    def almostEqualSet(self, s: str) -> Set[str]:
        print(f'AES({s})')
        L = tuple(s)

        answer = {s}        # start with s itself

        for i, A in enumerate(L):
            # print(f'    {i=} {A=}')
            for j in range(i + 1, len(L)):
                B = L[j]
                # print(f'      {j=} {B=}')
                if A == B:
                    continue
                S = self.swapped(L, i, j)
                # print(f'        {S=}')
                answer.add(
                    self.join(S)
                )

        print(f'{answer=}')
        return answer

    def countPairs(self, nums: List[int]) -> int:

        answer = 0

        # (A) deal with all numbers that are *actually* equal:

        numCounts = Counter(nums)
        # print(f'{numCounts=}')

        for N, count in numCounts.most_common():
            # sort by count descending
            if count == 1:
                # all other counts will also be 1
                break
            triangle = count * (count - 1) // 2     # == count choose 2
            # print(f'A: {N} * {count}: {triangle}')
            answer += triangle
        
        # (B) bucket up groups of numbers that share digits:

        buckets = {}
        maxNum = max(numCounts.keys())
        width = len(str(maxNum))
        for N in numCounts.keys():
            nStr = f'{N:0{width}}'
            # print(f'{nStr=}')
            Bucket = ''.join(sorted(nStr))
            # print(f'{Bucket=}')
            buckets.setdefault(Bucket, set())
            buckets[Bucket].add((N, nStr))
        # print(f'{buckets=}')
        buckets = {
            Bucket: Numbers
            for Bucket, Numbers in buckets.items()
            if len(Numbers) > 1
        }
        # print(f'{buckets=}')

        for Bucket, Numbers in buckets.items():
            Numbers = tuple(Numbers)    # need to keep their order during this work
            EqualitySet = [
                self.almostEqualSet(NSi)
                for (Ni, NSi) in Numbers
            ]
            for i, NTi in enumerate(Numbers):
                (Ni, NSi) = NTi
                countI = numCounts[Ni]
                esI = EqualitySet[i]
                # print(f'{i=} {countI=} {Ni=}')
                for j in range(i+1, len(Numbers)):
                    NTj = Numbers[j]
                    (Nj, NSj) = NTj
                    countJ = numCounts[Nj]
                    esJ = EqualitySet[j]
                    # print(f'  {j=} {countJ=} {Nj=}')
                    if esI & esJ:
                        # set intersection exists: they are "almost equal"
                        product = countI * countJ
                        # print(f'    B: {countI} * {countJ} = {product}')
                        answer += product
                    # else:
                    #     # print(f'    b: no; same bucket but not Almost Equal')
        
        return answer

26778