Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,8 @@

## Pystrings
* Anagram
* Group Anagrams
* [Test Group Anagrams](https://github.com/BrianLusina/PythonSnips/blob/master/pystrings/anagram/group_anagrams/test_group_anagrams.py)
* [Test Anagram](https://github.com/BrianLusina/PythonSnips/blob/master/pystrings/anagram/test_anagram.py)
* Balanced Paren
* [Test Balanced Paren](https://github.com/BrianLusina/PythonSnips/blob/master/pystrings/balanced_paren/test_balanced_paren.py)
Expand Down
22 changes: 22 additions & 0 deletions pystrings/anagram/group_anagrams/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Group Anagrams

Given a list of words or phrases, group the words that are anagrams of each other. An anagram is a word or phrase formed
from another word by rearranging its letters.

Constraints:

Let `strs` be the list of strings given as input to find the anagrams.

- 1 <= `strs.length` <=10^3
- 0 <= `strs[i].length` <= 100
- `strs[i]` consists of lowercase English letters

> Note the order in which the output is displayed doesn't matter

## Examples

![Example one](images/group_anagrams_example_one.png)
![Example two](images/group_anagrams_example_two.png)



69 changes: 69 additions & 0 deletions pystrings/anagram/group_anagrams/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from typing import List, Dict, Tuple
from collections import defaultdict


def group_anagrams_naive(strs: List[str]) -> List[List[str]]:
"""
Groups a list of strings by their anagrams.
Parameters:
strs (List[str]): A list of strings
Returns:
List[List[str]]: A list of lists, where each inner list contains strings that are anagrams of each other
"""
word_map: Dict[str, List[str]] = defaultdict(list)
# traversing the list of strings takes O(n) time where n is the number of strings in this list
for word in strs:
# Note that the sorting here takes O(nlog(n)) time were n is the number of characters in this string
key = "".join(sorted(word.lower()))
word_map[key].append(word)

return list(word_map.values())

def group_anagrams(strs: List[str]) -> List[List[str]]:
"""
Groups a list of strings by their anagrams.
This uses A better approach than sorting can be used to solve this problem. This solution involves computing the
frequency of each letter in every string. This will help reduce the time complexity of the given problem. We’ll just
compute the frequency of every string and store the strings in their respective list in a hash map.
We see that all members of each set are characterized by the same frequency of each letter. This means that the
frequency of each letter in the words belonging to the same group is equal. In the set [["speed", "spede"]], the
frequency of the characters s, p, e, and d are the same in each word.
Let’s see how we can implement the above algorithm:
- For each string, compute a 6-element list. Each element in this list represents the frequency of an English letter
in the corresponding string. This frequency count will be represented as a tuple. For example, "abbccc" will be
represented as (1, 2, 3, 0, 0, ..., 0). This mapping will generate identical lists for strings that are anagrams.
- Use this list as a key to insert the strings into a hash map. All anagrams will be mapped to the same key in this
hash map.
- While traversing each string, we generate its 26-element list and check if this list is present as a key in the
hash map. If it does, we'll append the string to the array corresponding to that key. Otherwise, we'll add the new
key-value pair to the hash map.
- Return the values of the hash map in a two-dimensional array, since each value will be an individual set of
anagrams.
Parameters:
strs (List[str]): A list of strings
Returns:
List[List[str]]: A list of lists, where each inner list contains strings that are anagrams of each other
"""
word_map: Dict[Tuple[int,...], List[str]] = defaultdict(list)
# traversing the list of strings takes O(n) time where n is the number of strings in this list
for word in strs:
count = [0] * 26
for char in word:
index = ord(char) - ord('a')
count[index] += 1

key = tuple(count)
word_map[key].append(word)

return list(word_map.values())
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
50 changes: 50 additions & 0 deletions pystrings/anagram/group_anagrams/test_group_anagrams.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import unittest
from . import group_anagrams


class GroupAnagramsTestCase(unittest.TestCase):
def test_1(self):
strs = ["eat", "beat", "neat", "tea"]
expected = [["eat", "tea"], ["beat"], ["neat"]]
actual = group_anagrams(strs)
self.assertEqual(expected, actual)

def test_2(self):
strs = ["duel", "dule", "speed", "spede", "deul", "cars"]
expected = [["duel", "dule", "deul"], ["speed", "spede"], ["cars"]]
actual = group_anagrams(strs)
self.assertEqual(expected, actual)

def test_3(self):
strs = ["eat","tea","tan","ate","nat","bat"]
expected = [["eat","tea","ate"],["tan","nat"],["bat"]]
actual = group_anagrams(strs)
self.assertEqual(expected, actual)

def test_4(self):
strs = ["word","sword","drow","rowd","iced","dice"]
expected = [["word","drow","rowd"],["sword"],["iced","dice"]]
actual = group_anagrams(strs)
self.assertEqual(expected, actual)

def test_5(self):
strs = ["eat","drink","sleep","repeat"]
expected = [["eat"],["drink"],["sleep"],["repeat"]]
actual = group_anagrams(strs)
self.assertEqual(expected, actual)

def test_6(self):
strs = ["hello","ohlle","dark"]
expected = [["hello","ohlle"],["dark"]]
actual = group_anagrams(strs)
self.assertEqual(expected, actual)

def test_7(self):
strs = ["eat","beat","neat","tea"]
expected = [["eat","tea"],["beat"],["neat"]]
actual = group_anagrams(strs)
self.assertEqual(expected, actual)


if __name__ == '__main__':
unittest.main()
Loading