Skip to content

Commit 63bc9eb

Browse files
committed
refactor(algorithms, two-pointers): add is subsequence algorithm
1 parent 7d744d3 commit 63bc9eb

7 files changed

Lines changed: 112 additions & 109 deletions

File tree

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Is Subsequence
2+
3+
Given two strings s and t, return true if s is a subsequence of t, or false otherwise.
4+
5+
A subsequence of a string is a new string that is formed from the original string by deleting some (can be none) of the
6+
characters without disturbing the relative positions of the remaining characters. (i.e., "ace" is a subsequence of "
7+
abcde" while "aec" is not).
8+
9+
``` plain
10+
Example 1:
11+
12+
Input: s = "abc", t = "ahbgdc"
13+
Output: true
14+
Example 2:
15+
16+
Input: s = "axc", t = "ahbgdc"
17+
Output: false
18+
```
19+
20+
## Related Topics
21+
22+
- Two Pointers
23+
- String
24+
- Dynamic Programming
25+
26+
27+
### Solution
28+
29+
The key intuition behind this problem is that we can use two pointers to match characters of s in t from left to right.
30+
We maintain a first pointer for string s and a second pointer for string t. As we scan through t, whenever the current
31+
character in t matches the current character we're looking for in s, we advance the first pointer to look for the next
32+
character. We always advance the second pointer regardless of a match. If by the end of the traversal the first pointer
33+
has reached the end of s, it means every character in s was found in t in the correct relative order, so s is a
34+
subsequence of t.
35+
36+
Now, let’s look at the solution steps below:
37+
38+
1. Initialize a pointer sPointer (pointing to the start of s) to 0
39+
2. Initialize a pointer tPointer (pointing to the start of t) to 0
40+
3. Iterate using a while loop as long as `pointer_s` is less than the length of s and `pointer_t` is less than the length
41+
of t.
42+
- If the character at `s[pointer_s]` equals the character at `t[pointer_t]`,
43+
- Increment `pointer_s` by 1
44+
- Regardless of whether there was a match, increment `pointer_t` by 1 to continue scanning through t.
45+
4. After the loop terminates, check if sPointer equals the length of s. If it does, all characters of s have been
46+
matched in order within t, so return TRUE. Otherwise, return FALSE.
47+
48+
#### Complexity analysis
49+
50+
##### Time complexity
51+
52+
The time complexity of the solution is O(n) where n is the length of t. In the worst case, we traverse the entire string
53+
t once with tPointer, performing a constant-time character comparison at each step.
54+
55+
##### Space complexity
56+
57+
The space complexity of the solution is O(1) because we only use two integer pointer variables (sPointer and tPointer)
58+
regardless of the input size. No additional data structures are allocated.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
def is_subsequence(s: str, t: str) -> bool:
2+
if len(s) == 0:
3+
return True
4+
5+
seek = 0
6+
7+
for i in range(len(t)):
8+
if s[seek] == t[i]:
9+
seek += 1
10+
if seek == len(s):
11+
return True
12+
13+
return False
14+
15+
16+
def is_subsequence_two_pointers(s: str, t: str) -> bool:
17+
pointer_s, pointer_t = 0, 0
18+
while pointer_s < len(s) and pointer_t < len(t):
19+
if s[pointer_s] == t[pointer_t]:
20+
pointer_s += 1
21+
pointer_t += 1
22+
23+
return pointer_s == len(s)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import unittest
2+
from parameterized import parameterized
3+
from utils.test_utils import custom_test_name_func
4+
from algorithms.two_pointers.issubsequence import (
5+
is_subsequence,
6+
is_subsequence_two_pointers,
7+
)
8+
9+
IS_SUBSEQUENCE_TEST_CASES = [
10+
("abc", "ahbgdc", True),
11+
("axc", "ahbgdc", False),
12+
("", "ahbgdc", True),
13+
("b", "abc", True),
14+
]
15+
16+
17+
class IsSubsequenceTestCases(unittest.TestCase):
18+
@parameterized.expand(IS_SUBSEQUENCE_TEST_CASES, name_func=custom_test_name_func)
19+
def test_is_subsequence(self, s: str, t: str, expected: bool):
20+
actual = is_subsequence(s, t)
21+
self.assertEqual(expected, actual)
22+
23+
@parameterized.expand(IS_SUBSEQUENCE_TEST_CASES, name_func=custom_test_name_func)
24+
def test_is_subsequence_two_pointers(self, s: str, t: str, expected: bool):
25+
actual = is_subsequence_two_pointers(s, t)
26+
self.assertEqual(expected, actual)
27+
28+
29+
if __name__ == "__main__":
30+
unittest.main()

pystrings/issubsequence/README.md

Lines changed: 0 additions & 24 deletions
This file was deleted.

pystrings/issubsequence/__init__.py

Lines changed: 0 additions & 25 deletions
This file was deleted.

pystrings/issubsequence/test_is_subsequence.py

Lines changed: 0 additions & 59 deletions
This file was deleted.

utils/test_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ def custom_test_name_func(testcase_func, param_num, param):
88

99
formatted_test_input = ""
1010
for idx, test_input in enumerate(test_inputs):
11-
formatted_test_input += f"(argument={idx}, input={test_input}) "
11+
formatted_test_input += f"(argument={idx}, value={test_input}) "
1212

1313
return f"{testcase_func.__name__}, test_number={param_num} inputs={formatted_test_input}, expected={expected}"

0 commit comments

Comments
 (0)