Problem 893

Abstract / Summary

Group strings that can become identical after any number of character swaps inside each string.

Note

The full problem description, including helpful examples, can be found on LeetCode.

Python 0893.py

class Solution:
    def numSpecialEquivGroups(self, words: List[str]) -> int:
        have = set()
        for w in words:
            even = sorted(w[ ::2])
            odd  = sorted(w[1::2])
            both = "".join(even + odd)
            have.add(both)
        return len(have)
9 lines | 293 bytes | submitted February 27, 2024 | GitHub   tab size 4 | low complexity

Difficulty

This problem is ranked as
easy medium hard

and has an acceptance rate of about 74% (80k+ submissions).

Typical patterns to solve this problem:
  • Array
  • Hash Table
  • Sorting
  • String
I solved 663 easy, 1250 medium and 309 hard problems

AI Disclaimer

Every single line of code was written by me, 100% from scratch, between August 2023 and August 2024.
However, two years later I set up this website and used various AI tools to generate most of the walkthrough / explanations below.

See this dedicated page for more details.
LeetCode profile | GitHub repo

Code Walkthrough

Overview

The provided implementation defines a class Solution with a single method numSpecialEquivGroups. The method receives a list of strings (words) and returns the count of distinct groups of words that are special-equivalent. Two words belong to the same group when one can be transformed into the other by any number of swaps of characters at even indices with other even indices, and similarly for odd indices. The algorithm normalizes each word by sorting the characters that appear at even positions separately from those at odd positions, concatenates the two sorted sequences, and stores the resulting canonical representation in a set. The final answer is the size of that set.

Key Steps

The core of the algorithm consists of three logical actions performed for each word in the input list.

Normalizing the even-position characters

even = sorted(w[::2])

The slice w[::2] extracts every character whose index is even (0, 2, 4, …). By applying sorted, the characters are placed in a deterministic order, eliminating any variation caused by permissible swaps among even positions. The resulting list even serves as the even-position component of the word’s canonical form.

Normalizing the odd-position characters

odd = sorted(w[1::2])

Analogously, the slice w[1::2] captures characters at odd indices (1, 3, 5, …). Sorting this sub-list yields a stable ordering for the odd-position component, neutralizing the effect of swaps that are allowed only among odd indices.

Building the canonical representation and collecting unique groups

both = "".join(even + odd)
have.add(both)

The two sorted lists are concatenated, and join converts the combined sequence into a single string. This string uniquely identifies the equivalence class of the original word because any two words that are special-equivalent will produce identical both values after the sorting steps. The set have accumulates these unique identifiers, automatically discarding duplicates.

Returning the count of distinct groups

return len(have)

After processing all input words, the size of the set reflects the number of distinct special-equivalence groups, which is the required result.

Edge Cases Handled

The algorithm gracefully handles several edge conditions without additional branching logic.

When a word is empty (""), both slices w[::2] and w[1::2] produce empty lists. Sorting empty lists yields empty lists, and concatenating them results in an empty string. Adding this empty string to the set correctly counts the empty word as a single group.

If a word consists of a single character, the even slice captures that character while the odd slice is empty. The canonical form becomes the single character itself, ensuring that any other single-character word with the same letter maps to the same group.

Words of odd length generate an even slice that is one element longer than the odd slice. The sorting and concatenation steps remain valid because each slice is processed independently, and the final string reflects the exact multiset of characters at even and odd positions.

The use of a set automatically eliminates duplicate canonical forms, so repeated words in the input list do not inflate the group count. This behavior also covers the case where all words are identical; the set will contain a single entry, and the method will return 1.

Performance Optimizations

The implementation leverages Python’s built-in data structures and operations to achieve efficient execution.

The slice notation (w[::2] and w[1::2]) extracts the required characters in linear time without explicit loops, allowing the interpreter to handle the indexing internally.

Sorting is performed on the two short sub-lists rather than on the entire word. For a word of length n, each slice contains roughly n/2 elements, so the total sorting work per word is comparable to sorting the whole word but benefits from the reduced constant factor of handling smaller lists.

The concatenation of the two sorted lists using the + operator creates a new list of length n before converting it to a string. This approach avoids repeated string concatenations, which would be costly due to immutability. By building the final string in a single join call, the code minimizes memory allocations.

Storing canonical forms in a set provides O(1) average-time insertion and membership checks, ensuring that the overall processing time scales linearly with the number of input words. The final len operation on the set is constant-time.

Collectively, these choices keep the algorithm’s runtime proportional to the total number of characters across all input words, while memory usage is bounded by the number of distinct groups, which is at most the number of input words.

written by openai gpt

ELI5 - Simplified Explanation

Imagine a big box of magical word blocks. Some words look different but are actually part of the same group because their letters can be swapped around like pieces in a puzzle. The computer looks at every word in the box and splits it into two piles. One pile gets the letters at the beginning, second, fourth, and sixth spots, and the other pile gets the rest. By putting those letters in order from shortest to tallest, the computer gives the word a secret special nickname. If two words end up with the same secret nickname, they belong to the same group. A special container keeps track of how many unique nicknames are found so everything stays neat and tidy. The final answer tells how many different groups are hiding inside the box.

written by google gemini

Code Quality

Several LLMs ranked my code quality on a scale from 1 (very poor) to 10 (outstanding):
n = 27 | median = 8 | μ = 8.04 | σ = 0.92

Complexity Analysis

Time: loglinear
As a rule of thumb, the maximum input size for one second execution time is
  • factorial: up to 10 elements
  • exponential: up to 20 elements
  • polynomial: a few thousand elements
  • loglinear: up to 100,000 elements
  • linear: a few million elements
  • logarithmic: a billion elements
  • constant: practically unlimited
generated automatically

Other Solutions

I wrote a basic crawler to find other solutions to that problem.
Obviously, I did that after I solved the problem myself.

In general, AlgoMonster and WalkCCC solutions are high-quality and always worth a look.
I haven't checked most of the other solutions, but they might be helpful as well.

And of course, the LeetCode forum is always a good place (or even the best) to look for alternative approaches.

(click on a column header to sort the table)
author
author solutions
www AlgoMonster C++ Java Python TS
WalkCCC C++ Java Python
leetcode.ca C++ Java Python Go
LeetcodeHelp C++ Java Python
Progiez C++ Java Python
github BigEggStudy C#
black-shadows C++
charles-wangkai Java
everthis JS
gouthampradhan Java
JoshCrozier JS
keineahnung2345 C++
lkwq007 Python
lzl124631x C++
ruoru Rust
suxrobGM C#
varunu28 Java
zfang399 C++
AlgoMonster C++, Python, Java, TypeScript
WalkCCC C++, Python, Java
leetcode.ca C++, Go, Java, Python
LeetcodeHelp C++, Java, Python
Progiez C++, Java, Python
BigEggStudy C#
black-shadows C++, C++
charles-wangkai Java
everthis JavaScript
gouthampradhan Java
JoshCrozier JavaScript
keineahnung2345 C++
lkwq007 Python
lzl124631x C++
ruoru Rust
suxrobGM C#
varunu28 Java
zfang399 C++
30 links | 18 authors | 8 languages

Videos

2 videos   did my crawler come up with invalid videos ? drop me a line.

Solved On The Same Day

3 medium 1 hard
5 solutions   February 27, 2024

Extra

There are a few extra pages giving you some insights into my setup:
  • Tech Stack - my development environment and tools
  • AI - how AI helped me create this website (NOT solving the problems)
  • Progress - progression over time
  • Quality - code quality