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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,18 @@
* [Test Next Permutation](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/next_permutation/test_next_permutation.py)
* Pair With Sum In Array
* [Test Pair With Sum In Array](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/pair_with_sum_in_array/test_pair_with_sum_in_array.py)
* Palindrome
* Largest Palindrome Product
* [Test Largest Palindrome Product](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/palindrome/largest_palindrome_product/test_largest_palindrome_product.py)
* [Longest Palindrome](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/palindrome/longest_palindrome.py)
* [Longest Palindromic Substring](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/palindrome/longest_palindromic_substring.py)
* [Palindrome Index](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/palindrome/palindrome_index.py)
* [Palindrome Pairs](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/palindrome/palindrome_pairs.py)
* Permutation Palindrome
* [Test Permutation Palindrome](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/palindrome/permutation_palindrome/test_permutation_palindrome.py)
* [Test Palindrome](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/palindrome/test_palindrome.py)
* [Test Palindrome Index](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/palindrome/test_palindrome_index.py)
* [Test Palindrome Pairs](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/palindrome/test_palindrome_pairs.py)
* Rain Water Trapped
* [Test Trapped Rain Water](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/rain_water_trapped/test_trapped_rain_water.py)
* Reverse Array
Expand Down Expand Up @@ -844,18 +856,6 @@
* [Test Max Vowels In Substring](https://github.com/BrianLusina/PythonSnips/blob/master/pystrings/max_vowels_in_substring/test_max_vowels_in_substring.py)
* Merge Strings
* [Test Merge Strings](https://github.com/BrianLusina/PythonSnips/blob/master/pystrings/merge_strings/test_merge_strings.py)
* Palindrome
* Largest Palindrome Product
* [Test Largest Palindrome Product](https://github.com/BrianLusina/PythonSnips/blob/master/pystrings/palindrome/largest_palindrome_product/test_largest_palindrome_product.py)
* [Longest Palindrome](https://github.com/BrianLusina/PythonSnips/blob/master/pystrings/palindrome/longest_palindrome.py)
* [Longest Palindromic Substring](https://github.com/BrianLusina/PythonSnips/blob/master/pystrings/palindrome/longest_palindromic_substring.py)
* [Palindrome Index](https://github.com/BrianLusina/PythonSnips/blob/master/pystrings/palindrome/palindrome_index.py)
* [Palindrome Pairs](https://github.com/BrianLusina/PythonSnips/blob/master/pystrings/palindrome/palindrome_pairs.py)
* Permutation Palindrome
* [Test Permutation Palindrome](https://github.com/BrianLusina/PythonSnips/blob/master/pystrings/palindrome/permutation_palindrome/test_permutation_palindrome.py)
* [Test Palindrome](https://github.com/BrianLusina/PythonSnips/blob/master/pystrings/palindrome/test_palindrome.py)
* [Test Palindrome Index](https://github.com/BrianLusina/PythonSnips/blob/master/pystrings/palindrome/test_palindrome_index.py)
* [Test Palindrome Pairs](https://github.com/BrianLusina/PythonSnips/blob/master/pystrings/palindrome/test_palindrome_pairs.py)
* Pangram
* [Test Pangram Checker](https://github.com/BrianLusina/PythonSnips/blob/master/pystrings/pangram/test_pangram_checker.py)
* Parenthesis
Expand Down
2 changes: 1 addition & 1 deletion algorithms/backtracking/partition_string/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from typing import List
from pystrings.palindrome import is_palindrome
from algorithms.two_pointers.palindrome import is_palindrome


def partition(s: str) -> List[List[str]]:
Expand Down
8 changes: 4 additions & 4 deletions algorithms/greedy/jump_game/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,17 @@ target remains beyond index 0, then no path exists from the start to the end, an

#### Algorithm

1. We begin by setting the last index of the array as our initial target using the variable target = len(nums) - 1. This
1. We begin by setting the last index of the array as our initial target using the variable `target = len(nums) - 1`. This
target represents the position we are trying to reach, starting from the end and working backward. By initializing the
target this way, we define our goal: to find out if there is any index i from which the target is reachable based on the
value at that position, nums[i]. This also sets the stage for updating the target if such an index is found.

2. Next, we loop backward through the array using for i in range(len(nums) - 2, -1, -1). Here, i represents the current
2. Next, we loop backward through the array using `for i in range(len(nums) - 2, -1, -1)`. Here, `i` represents the current
index we are analyzing. At each index i, the value nums[i] tells us how far we can jump forward from that position.
By checking whether i + nums[i] >= target, we determine whether it can reach the current target from index i. This
By checking whether `i + nums[i] >= target`, we determine whether it can reach the current target from index i. This
step allows us to use the jump range at each position to decide if it can potentially lead us to the end.

3. If the condition i + nums[i] >= target is TRUE, the current index i can jump far enough to reach the current target.
3. If the condition `i + nums[i] >= target` is TRUE, the current index i can jump far enough to reach the current target.
In that case, we update target = i, effectively saying, “Now we just need to reach index i instead.” If the condition
fails, we move back in the array one step further and try again with the previous index.
We repeat this process until we either:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,82 @@ As only a few variables are used, the space complexity of this solution is O(1).
- String
- Dynamic Programming
- Two Pointers

---

## Valid Palindrome II

Write a function that takes a string as input and checks whether it can be a valid palindrome by removing at most one
character from it.

### Constraints

- 1 <= `s.length` <= 10^5
- The string only consists of English letters

### Examples

Example 1:

```text
Input: s = "aba"
Output: true
```

Example 2:

```text
Input: s = "abca"
Output: true
Explanation: You could delete the character 'c'.
```

Example 3:

```text
Input: s = "abc"
Output: false
```

### Solution

The algorithm uses a two-pointer technique to determine whether a string is a palindrome or can be transformed into it
by removing at most one character, where one pointer starts at the beginning and the other at the end. As the pointers
move toward each other, they compare the corresponding characters. If all pairs match, the string is a palindrome.
However, if a mismatch is detected, the algorithm explores two possibilities: removing the character at either pointer
and checking if the resulting substring forms a palindrome. If either check passes, the function returns TRUE; otherwise,
it returns FALSE.

The algorithm consists of two functions:

- `is_substring_palindrome(left, right)`: This helper function checks if a substring of the input string, defined by
`left` and `right` indexes, is a palindrome. It uses a two-pointer approach, comparing characters from both ends
inward. If any mismatch is found, it returns FALSE; otherwise, it returns TRUE.
- `is_valid_palindrome_with_one_char_removal(string)`: This function checks if the entire string is a palindrome or can
become one by removing one character. It initializes two pointers, `left_pointer` at the start and `right_pointer` at
end of the string:
- If the characters at the `left_pointer` and `right_pointer` are the same, it moves both pointers inward.
- If the characters differ, it checks two cases by calling is_substring_palindrome:
- The substring from `left_pointer + 1` to `right_pointer`
- The substring from `left_pointer` to `right_pointer - 1`
- If either case returns TRUE, the function returns TRUE; otherwise, it returns FALSE.

If the traversal completes without finding a mismatch, the string is a palindrome, and the function returns TRUE.

![Solution 1](./images/solutions/is_valid_palindrome_with_one_char_removal_solution_1.png)
![Solution 2](./images/solutions/is_valid_palindrome_with_one_char_removal_solution_2.png)
![Solution 3](./images/solutions/is_valid_palindrome_with_one_char_removal_solution_3.png)
![Solution 4](./images/solutions/is_valid_palindrome_with_one_char_removal_solution_4.png)
![Solution 5](./images/solutions/is_valid_palindrome_with_one_char_removal_solution_5.png)
![Solution 6](./images/solutions/is_valid_palindrome_with_one_char_removal_solution_6.png)
![Solution 7](./images/solutions/is_valid_palindrome_with_one_char_removal_solution_7.png)
![Solution 8](./images/solutions/is_valid_palindrome_with_one_char_removal_solution_8.png)
![Solution 9](./images/solutions/is_valid_palindrome_with_one_char_removal_solution_9.png)

#### Time Complexity

The time complexity of the solution above is O(n), where n is the length of the string

#### Space Complexity

The space complexity of solution above is O(1).
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,45 @@ def is_palindrome_number_2(x: int) -> bool:
left += 1
right -= 1
return True


def is_valid_palindrome_with_one_char_removal(s: str) -> bool:
"""
Checks if a string s can become a valid palindrome with at most one character removal. Returns True if it can, False
otherwise
Args:
s (str): string to check
Returns:
bool: True if after removal of at most one character, the string becomes a valid palindrome, otherwise False
"""
left_pointer = 0
right_pointer = len(s) - 1

def is_substring_palindrome(left: int, right: int) -> bool:
"""
Checks if a given substring is a valid palindrome
Args:
left(int): the left index on the string to check
right(int): the right index on the string to check
Returns:
bool: True if the substring is a valid palindrome, else False
"""
while left < right:
if s[left] != s[right]:
return False
left += 1
right -= 1
return True

while left_pointer < right_pointer:
if s[left_pointer] != s[right_pointer]:
# validate that the substrings are valid palindromes
return is_substring_palindrome(
left_pointer + 1, right_pointer
) or is_substring_palindrome(left_pointer, right_pointer - 1)

# move the pointers as the characters are equal
left_pointer += 1
right_pointer -= 1

return True
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.
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.
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.
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.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
# -*- coding: utf-8 -*-
import unittest
from random import choice, randint
from string import ascii_letters
from typing import Union
from parameterized import parameterized
from pystrings.palindrome import (
from algorithms.two_pointers.palindrome import (
is_palindrome,
smallest_palindrome,
largest_palindrome,
is_palindrome_number,
is_palindrome_number_2,
is_valid_palindrome_with_one_char_removal,
)
from pystrings.palindrome.longest_palindrome import longest_palindrome
from algorithms.two_pointers.palindrome.longest_palindrome import longest_palindrome


class LongestPalindromeTests(unittest.TestCase):
Expand Down Expand Up @@ -141,5 +141,24 @@ def test_largest_palindrome_from_triple_digit_factors(self):
self.assertEqual({913, 993}, set(factors))


IS_PALINDROME_WITH_ONE_CHAR_REMOVAL_TEST_CASES = [
("aba", True),
("abca", True),
("abc", False),
("abccbxa", True),
("madame", True),
("dead", True),
("tebbem", False),
("eeccccbebaeeabebccceea", False),
]


class IsPalindromeWithOneCharRemoval(unittest.TestCase):
@parameterized.expand(IS_PALINDROME_WITH_ONE_CHAR_REMOVAL_TEST_CASES)
def test_is_palindrome_with_one_char_removal(self, s: str, expected: bool):
actual = is_valid_palindrome_with_one_char_removal(s)
self.assertEqual(expected, actual)


if __name__ == "__main__":
unittest.main()
10 changes: 5 additions & 5 deletions datastructures/trees/binary/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ When they meet, that meeting point is their lowest common ancestor.

The steps of the algorithm are as follows:

1. Initialize two pointers: ptr1 starting at p and ptr2 starting at q.
2. While ptr1 and ptr2 are not pointing to the same node:
- If ptr1 has a parent, move ptr1 to ptr1.parent; otherwise, set ptr1 = q.
- If ptr2 has a parent, move ptr2 to ptr2.parent; otherwise, set ptr2 = p.
1. Initialize two pointers: `ptr1` starting at `p` and `ptr2` starting at `q`.
2. While `ptr1` and `ptr2` are not pointing to the same node:
- If `ptr1` has a parent, move `ptr1` to `ptr1.parent;` otherwise, set `ptr1 = q`.
- If `ptr2` has a parent, move `ptr2` to `ptr2.parent`; otherwise, set `ptr2 = p`.

3. When ptr1 == ptr2, return ptr1. This node is the lowest common ancestor (LCA) of p and q.
3. When `ptr1 == ptr2`, return `ptr1`. This node is the lowest common ancestor (LCA) of p and q.

Let’s look at the following illustration to get a better understanding of the solution:

Expand Down
5 changes: 4 additions & 1 deletion tests/pystrings/test_longest_palindrome.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import unittest

from pystrings.palindrome import longest_palindrome_one, longest_palindrome_two
from algorithms.two_pointers.palindrome import (
longest_palindrome_one,
longest_palindrome_two,
)


class LongestPalindromeV1TestCases(unittest.TestCase):
Expand Down
Loading