diff --git a/DIRECTORY.md b/DIRECTORY.md index 528220a5..c79ace86 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -707,6 +707,8 @@ * [Test Greatest Common Divisor](https://github.com/BrianLusina/PythonSnips/blob/master/pystrings/greatest_common_divisor/test_greatest_common_divisor.py) * Inttostr * [Test Int To Str](https://github.com/BrianLusina/PythonSnips/blob/master/pystrings/inttostr/test_int_to_str.py) + * Is Prefix + * [Test Is Prefix Of Word](https://github.com/BrianLusina/PythonSnips/blob/master/pystrings/is_prefix/test_is_prefix_of_word.py) * Is Unique * [Test Is Unique](https://github.com/BrianLusina/PythonSnips/blob/master/pystrings/is_unique/test_is_unique.py) * Issubsequence diff --git a/algorithms/fast_and_slow/happy_number/README.md b/algorithms/fast_and_slow/happy_number/README.md index a38bfa88..3887f547 100644 --- a/algorithms/fast_and_slow/happy_number/README.md +++ b/algorithms/fast_and_slow/happy_number/README.md @@ -15,26 +15,26 @@ Return TRUE if n is a happy number, and FALSE if not. ### Sample Example 1 -![Sample Example 1.1](example_1_1.png) -![Sample Example 1.2](example_1_2.png) -![Sample Example 1.3](example_1_3.png) +![Sample Example 1.1](images/example/example_1_1.png) +![Sample Example 1.2](images/example/example_1_2.png) +![Sample Example 1.3](images/example/example_1_3.png) ### Sample Example 2 -![Sample Example 2.1](example_2_1.png) -![Sample Example 2.2](example_2_2.png) -![Sample Example 2.3](example_2_3.png) +![Sample Example 2.1](images/example/example_2_1.png) +![Sample Example 2.2](images/example/example_2_2.png) +![Sample Example 2.3](images/example/example_2_3.png) ## Solution Example Below shows an example using Floyd's Cycle Detection Algorithm or Tortoise and Hare algorithm to detect a cycle for the number 2. -![Solution Example 1](solution_example_1.png) -![Solution Example 2](solution_example_2.png) -![Solution Example 3](solution_example_3.png) -![Solution Example 4](solution_example_4.png) -![Solution Example 5](solution_example_5.png) -![Solution Example 6](solution_example_6.png) -![Solution Example 7](solution_example_7.png) -![Solution Example 8](solution_example_8.png) +![Solution Example 1](images/solution/solution_example_1.png) +![Solution Example 2](images/solution/solution_example_2.png) +![Solution Example 3](images/solution/solution_example_3.png) +![Solution Example 4](images/solution/solution_example_4.png) +![Solution Example 5](images/solution/solution_example_5.png) +![Solution Example 6](images/solution/solution_example_6.png) +![Solution Example 7](images/solution/solution_example_7.png) +![Solution Example 8](images/solution/solution_example_8.png) diff --git a/algorithms/fast_and_slow/happy_number/example_1_1.png b/algorithms/fast_and_slow/happy_number/images/example/example_1_1.png similarity index 100% rename from algorithms/fast_and_slow/happy_number/example_1_1.png rename to algorithms/fast_and_slow/happy_number/images/example/example_1_1.png diff --git a/algorithms/fast_and_slow/happy_number/example_1_2.png b/algorithms/fast_and_slow/happy_number/images/example/example_1_2.png similarity index 100% rename from algorithms/fast_and_slow/happy_number/example_1_2.png rename to algorithms/fast_and_slow/happy_number/images/example/example_1_2.png diff --git a/algorithms/fast_and_slow/happy_number/example_1_3.png b/algorithms/fast_and_slow/happy_number/images/example/example_1_3.png similarity index 100% rename from algorithms/fast_and_slow/happy_number/example_1_3.png rename to algorithms/fast_and_slow/happy_number/images/example/example_1_3.png diff --git a/algorithms/fast_and_slow/happy_number/example_2_1.png b/algorithms/fast_and_slow/happy_number/images/example/example_2_1.png similarity index 100% rename from algorithms/fast_and_slow/happy_number/example_2_1.png rename to algorithms/fast_and_slow/happy_number/images/example/example_2_1.png diff --git a/algorithms/fast_and_slow/happy_number/example_2_2.png b/algorithms/fast_and_slow/happy_number/images/example/example_2_2.png similarity index 100% rename from algorithms/fast_and_slow/happy_number/example_2_2.png rename to algorithms/fast_and_slow/happy_number/images/example/example_2_2.png diff --git a/algorithms/fast_and_slow/happy_number/example_2_3.png b/algorithms/fast_and_slow/happy_number/images/example/example_2_3.png similarity index 100% rename from algorithms/fast_and_slow/happy_number/example_2_3.png rename to algorithms/fast_and_slow/happy_number/images/example/example_2_3.png diff --git a/algorithms/fast_and_slow/happy_number/solution_example_1.png b/algorithms/fast_and_slow/happy_number/images/solution/solution_example_1.png similarity index 100% rename from algorithms/fast_and_slow/happy_number/solution_example_1.png rename to algorithms/fast_and_slow/happy_number/images/solution/solution_example_1.png diff --git a/algorithms/fast_and_slow/happy_number/solution_example_2.png b/algorithms/fast_and_slow/happy_number/images/solution/solution_example_2.png similarity index 100% rename from algorithms/fast_and_slow/happy_number/solution_example_2.png rename to algorithms/fast_and_slow/happy_number/images/solution/solution_example_2.png diff --git a/algorithms/fast_and_slow/happy_number/solution_example_3.png b/algorithms/fast_and_slow/happy_number/images/solution/solution_example_3.png similarity index 100% rename from algorithms/fast_and_slow/happy_number/solution_example_3.png rename to algorithms/fast_and_slow/happy_number/images/solution/solution_example_3.png diff --git a/algorithms/fast_and_slow/happy_number/solution_example_4.png b/algorithms/fast_and_slow/happy_number/images/solution/solution_example_4.png similarity index 100% rename from algorithms/fast_and_slow/happy_number/solution_example_4.png rename to algorithms/fast_and_slow/happy_number/images/solution/solution_example_4.png diff --git a/algorithms/fast_and_slow/happy_number/solution_example_5.png b/algorithms/fast_and_slow/happy_number/images/solution/solution_example_5.png similarity index 100% rename from algorithms/fast_and_slow/happy_number/solution_example_5.png rename to algorithms/fast_and_slow/happy_number/images/solution/solution_example_5.png diff --git a/algorithms/fast_and_slow/happy_number/solution_example_6.png b/algorithms/fast_and_slow/happy_number/images/solution/solution_example_6.png similarity index 100% rename from algorithms/fast_and_slow/happy_number/solution_example_6.png rename to algorithms/fast_and_slow/happy_number/images/solution/solution_example_6.png diff --git a/algorithms/fast_and_slow/happy_number/solution_example_7.png b/algorithms/fast_and_slow/happy_number/images/solution/solution_example_7.png similarity index 100% rename from algorithms/fast_and_slow/happy_number/solution_example_7.png rename to algorithms/fast_and_slow/happy_number/images/solution/solution_example_7.png diff --git a/algorithms/fast_and_slow/happy_number/solution_example_8.png b/algorithms/fast_and_slow/happy_number/images/solution/solution_example_8.png similarity index 100% rename from algorithms/fast_and_slow/happy_number/solution_example_8.png rename to algorithms/fast_and_slow/happy_number/images/solution/solution_example_8.png diff --git a/algorithms/fast_and_slow/happy_number/test_happy_number.py b/algorithms/fast_and_slow/happy_number/test_happy_number.py index 2cdc8bb6..3bde6423 100644 --- a/algorithms/fast_and_slow/happy_number/test_happy_number.py +++ b/algorithms/fast_and_slow/happy_number/test_happy_number.py @@ -113,6 +113,12 @@ def test_9(self): actual = is_happy_number_2(number) self.assertFalse(actual) + def test_10(self): + """should return false for 20""" + number = 20 + actual = is_happy_number_2(number) + self.assertFalse(actual) + if __name__ == "__main__": unittest.main() diff --git a/datastructures/streams/stream_checker/__init__.py b/datastructures/streams/stream_checker/__init__.py index 02d7d02f..78687c03 100644 --- a/datastructures/streams/stream_checker/__init__.py +++ b/datastructures/streams/stream_checker/__init__.py @@ -58,7 +58,7 @@ def query(self, letter: str) -> bool: # Iterate stream in reverse (newest character first) for character in reversed(self.stream): - # Check for dead end (critical for query logic) + # Check for dead-end (critical for query logic) if character not in current.children: return False diff --git a/datastructures/trees/trie/trie.py b/datastructures/trees/trie/trie.py index c1b0e2de..76982aa6 100644 --- a/datastructures/trees/trie/trie.py +++ b/datastructures/trees/trie/trie.py @@ -1,4 +1,4 @@ -from typing import List +from typing import List, Optional from datastructures.trees.trie.trie_node import TrieNode @@ -6,25 +6,54 @@ class Trie: def __init__(self): self.root = TrieNode() - def insert(self, word: str) -> None: + def insert(self, word: str, index: Optional[int] = None) -> None: + """ + Inserts a word into the Trie. This has an optional index argument that allows + us to track the index of the word in the original list. So, if inserting words from a list such as "The author + is smart", the index of "smart" would be 3. If this is not provided, i.e. None, then each node in the tree will + have an index of None. Note that for each character in the word, we update the index of the node to the index of + the word. For example: + + sentence = "please playground player" + + Insert "please" (index 0): + p(0) → l(0) → e(0) → a(0) → s(0) → e(0) + + Insert "playground" (index 1): + p(0) → l(0) → a(1) → y(1) → g(1) → ... + ↘ + e(0) → a(0) → s(0) → e(0) + + Insert "player" (index 2): + p(0) → l(0) → a(1) → y(1) → ... + ↘ + e(2) → r(2) + + Index then tracks the earliest index of the word in the original list. So, for the example above, the index of + "player" would be 2, not 0. + + Parameters: + word (str): The word to insert + index (Optional[int]): The index of the word (default is None) + + Returns: + None + """ curr = self.root for char in word: - if char in curr.children: - curr = curr.children[char] - else: - new_node = TrieNode() - curr.children[char] = new_node - curr = new_node + curr = curr.children[char] + if index is not None: + curr.index = min(curr.index or float("inf"), index) curr.is_end = True def search(self, word: str) -> List[str]: - curr = self.root - if len(word) == 0: return [] + curr = self.root + for char in word: if char in curr.children: curr = curr.children[char] @@ -35,10 +64,10 @@ def search(self, word: str) -> List[str]: def dfs(node: TrieNode, prefix: str) -> None: if node.is_end: - output.append((prefix + node.char)) + output.append((prefix + "".join(node.children.keys()))) for child in node.children.values(): - dfs(child, prefix + node.char) + dfs(child, prefix + "".join(node.children.keys())) dfs(curr, word[:-1]) return output @@ -55,3 +84,6 @@ def starts_with(self, prefix: str) -> bool: curr = curr.children[char] return True + + def __repr__(self): + return f"Trie(root={self.root})" diff --git a/datastructures/trees/trie/trie_node.py b/datastructures/trees/trie/trie_node.py index bcab413b..59d05731 100644 --- a/datastructures/trees/trie/trie_node.py +++ b/datastructures/trees/trie/trie_node.py @@ -1,4 +1,4 @@ -from typing import DefaultDict +from typing import DefaultDict, Optional from collections import defaultdict @@ -18,6 +18,45 @@ def __init__(self): """ self.children: DefaultDict[str, TrieNode] = defaultdict(TrieNode) self.is_end = False + self.index: Optional[int] = None def __repr__(self): - return f"TrieNode({self.children.items()}, {self.is_end})" + return f"TrieNode(children={self.children.items()}, index={self.index}, is_end={self.is_end})" + + def insert(self, word: str, index: int): + """ + Inserts a word into the TrieNode. + + Parameters: + word (str): The word to insert + index (int): The index of the word + + Returns: + None + """ + curr = self + for char in word: + curr = curr.children[char] + curr.index = min(curr.index or float("inf"), index) + curr.is_end = True + + def search_prefix(self, prefix: str) -> int: + """ + Searches for a prefix in the TrieNode. + + Parameters: + prefix (str): The prefix to search for + + Returns: + int: The index of the word if the prefix is found, -1 otherwise + """ + current = self + + for char in prefix: + if char not in current.children: + return -1 + + # Traverse to the next node + current = current.children[char] + + return current.index if current.index is not None else -1 diff --git a/pystrings/is_prefix/README.md b/pystrings/is_prefix/README.md new file mode 100644 index 00000000..97c3efd0 --- /dev/null +++ b/pystrings/is_prefix/README.md @@ -0,0 +1,24 @@ +# Check if a Word is a Prefix of Any Word in a Sentence + +You are given a sentence containing words separated by single spaces and a searchWord. Your task is to determine whether +searchWord is a prefix of any word in the sentence. +Return the 1-based index of the first word in which searchWord appears as a prefix. + +- If searchWord is a prefix of multiple words, return the index of the earliest such word. +- If no word starts with searchWord, return −1 + +> A prefix of a string is any contiguous substring that begins at the first character. + +Constraints: + +- 1 <= sentence.length <= 100 +- 1 <= search_word.length <= 10 +- The sentence consists of lowercase English letters and spaces. +- search_word consists of lowercase English letters. + +## Examples + +![Example 1](./images/examples/is_prefix_example_1.png) +![Example 2](./images/examples/is_prefix_example_2.png) +![Example 3](./images/examples/is_prefix_example_3.png) +![Example 4](./images/examples/is_prefix_example_4.png) diff --git a/pystrings/is_prefix/__init__.py b/pystrings/is_prefix/__init__.py new file mode 100644 index 00000000..0932c70d --- /dev/null +++ b/pystrings/is_prefix/__init__.py @@ -0,0 +1,23 @@ +from datastructures.trees.trie.trie_node import TrieNode + + +def is_prefix_of_word(sentence: str, search_word: str) -> int: + """ + This function will check if a given search_word is a prefix of any word in a sentence. + + Parameters: + sentence (str): The sentence to search in. + search_word (str): The prefix to search for. + + Returns: + int: The index of the word if the prefix is found, -1 otherwise. + """ + trie = TrieNode() + word_list = sentence.split(" ") + + # Insert the words into the trie with their respective index + for index, word in enumerate(word_list, start=1): + trie.insert(word, index) + + # Search for the prefix in the trie + return trie.search_prefix(search_word) diff --git a/pystrings/is_prefix/images/examples/is_prefix_example_1.png b/pystrings/is_prefix/images/examples/is_prefix_example_1.png new file mode 100644 index 00000000..2eaa7b6c Binary files /dev/null and b/pystrings/is_prefix/images/examples/is_prefix_example_1.png differ diff --git a/pystrings/is_prefix/images/examples/is_prefix_example_2.png b/pystrings/is_prefix/images/examples/is_prefix_example_2.png new file mode 100644 index 00000000..b1a497d1 Binary files /dev/null and b/pystrings/is_prefix/images/examples/is_prefix_example_2.png differ diff --git a/pystrings/is_prefix/images/examples/is_prefix_example_3.png b/pystrings/is_prefix/images/examples/is_prefix_example_3.png new file mode 100644 index 00000000..bf3db009 Binary files /dev/null and b/pystrings/is_prefix/images/examples/is_prefix_example_3.png differ diff --git a/pystrings/is_prefix/images/examples/is_prefix_example_4.png b/pystrings/is_prefix/images/examples/is_prefix_example_4.png new file mode 100644 index 00000000..3b04972b Binary files /dev/null and b/pystrings/is_prefix/images/examples/is_prefix_example_4.png differ diff --git a/pystrings/is_prefix/test_is_prefix_of_word.py b/pystrings/is_prefix/test_is_prefix_of_word.py new file mode 100644 index 00000000..fe402595 --- /dev/null +++ b/pystrings/is_prefix/test_is_prefix_of_word.py @@ -0,0 +1,57 @@ +import unittest +from . import is_prefix_of_word + + +class IsPrefixOfWordTestCase(unittest.TestCase): + def test_1(self): + sentence = "i love coding" + search_word = "lov" + expected = 2 + actual = is_prefix_of_word(sentence, search_word) + self.assertEqual(expected, actual) + + def test_2(self): + sentence = "hello world" + search_word = "he" + expected = 1 + actual = is_prefix_of_word(sentence, search_word) + self.assertEqual(expected, actual) + + def test_3(self): + sentence = "please playground player" + search_word = "pla" + expected = 2 + actual = is_prefix_of_word(sentence, search_word) + self.assertEqual(expected, actual) + + def test_4(self): + sentence = "open source ai" + search_word = "deep" + expected = -1 + actual = is_prefix_of_word(sentence, search_word) + self.assertEqual(expected, actual) + + def test_5(self): + sentence = "cats dog cattle category" + search_word = "cat" + expected = 1 + actual = is_prefix_of_word(sentence, search_word) + self.assertEqual(expected, actual) + + def test_6(self): + sentence = "this is fine" + search_word = "fi" + expected = 3 + actual = is_prefix_of_word(sentence, search_word) + self.assertEqual(expected, actual) + + def test_7(self): + sentence = "hello world" + search_word = "x" + expected = -1 + actual = is_prefix_of_word(sentence, search_word) + self.assertEqual(expected, actual) + + +if __name__ == '__main__': + unittest.main()