3083. Existence of a Substring in a String and Its Reverse
Problem Description
You are given a string s
. Your task is to determine if there exists any substring of length 2 that appears in both the original string s
and its reverse.
A substring of length 2 consists of two consecutive characters from the string. For example, if s = "abc"
, the substrings of length 2 are "ab"
and "bc"
.
The reverse of a string is obtained by reading the characters from right to left. For example, if s = "abc"
, its reverse is "cba"
.
You need to check if any 2-character substring from s
also appears as a substring in the reversed version of s
. If such a substring exists, return true
; otherwise, return false
.
For example:
- If
s = "abcd"
, the reverse is"dcba"
. The substring"ab"
from the original does not appear in"dcba"
,"bc"
does not appear in"dcba"
, and"cd"
does not appear in"dcba"
. So the answer would befalse
. - If
s = "aba"
, the reverse is"aba"
. The substring"ab"
appears in both the original and reverse, so the answer would betrue
.
The solution uses a hash table to store all 2-character substrings from the reversed string, then checks if any 2-character substring from the original string exists in this set. The pairwise
function is used to generate consecutive pairs of characters from the string.
Intuition
The key insight is that we need to find if any 2-character substring appears in both the original string and its reverse. Instead of checking every possible combination, we can think of this as a set intersection problem.
First, let's understand what we're looking for. If a substring "xy"
appears in the original string s
, and we want it to also appear in the reverse of s
, then somewhere in the reversed string there must be the same pattern "xy"
.
The straightforward approach would be to:
- Extract all 2-character substrings from the original string
- Extract all 2-character substrings from the reversed string
- Check if there's any common substring between these two sets
Since we're looking for any match (not all matches), we can optimize this by:
- First collecting all 2-character substrings from the reversed string into a set for O(1) lookup
- Then iterating through the original string's 2-character substrings and checking if any exists in our set
The use of pairwise
function elegantly generates consecutive character pairs. For a string like "abc"
, pairwise
would give us ('a','b')
and ('b','c')
. By storing these pairs as tuples in a set from the reversed string, we can quickly check membership when iterating through pairs from the original string.
This approach is efficient because:
- We only need to traverse each string once
- Set operations (insertion and lookup) are O(1) on average
- We can return immediately upon finding the first match, avoiding unnecessary computation
Solution Approach
The solution implements a hash table-based approach to efficiently find common 2-character substrings between a string and its reverse.
Step 1: Extract substrings from the reversed string
st = {(a, b) for a, b in pairwise(s[::-1])}
s[::-1]
reverses the strings
pairwise()
generates consecutive character pairs from the reversed string- We use a set comprehension to store all these pairs as tuples in a hash set
st
- For example, if
s = "abcd"
, thens[::-1] = "dcba"
, andst
would contain{('d','c'), ('c','b'), ('b','a')}
Step 2: Check for matching substrings in the original string
return any((a, b) in st for a, b in pairwise(s))
pairwise(s)
generates consecutive character pairs from the original string- For each pair
(a, b)
, we check if it exists in the setst
using thein
operator (O(1) lookup) - The
any()
function returnsTrue
as soon as it finds the first matching pair, providing early termination - If no matching pair is found after checking all pairs, it returns
False
Data Structures Used:
- Set (Hash Table): Stores the 2-character substrings from the reversed string as tuples, enabling O(1) average-case lookup time
- Tuple: Each 2-character substring is represented as a tuple
(char1, char2)
for immutability and hashability
Time Complexity: O(n) where n is the length of the string, as we traverse the string twice (once for reverse, once for original)
Space Complexity: O(n) for storing the set of 2-character substrings from the reversed string
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with s = "abcba"
:
Step 1: Reverse the string and extract 2-character substrings
- Original string:
"abcba"
- Reversed string:
"abcba"
(it's a palindrome in this case) - Apply
pairwise()
to reversed string to get consecutive pairs:- Position 0-1:
('a', 'b')
- Position 1-2:
('b', 'c')
- Position 2-3:
('c', 'b')
- Position 3-4:
('b', 'a')
- Position 0-1:
- Store these in set
st = {('a','b'), ('b','c'), ('c','b'), ('b','a')}
Step 2: Check original string's 2-character substrings
- Apply
pairwise()
to original string"abcba"
:- Position 0-1:
('a', 'b')
→ Check: Is('a','b')
inst
? YES! ✓
- Position 0-1:
- Since we found a match,
any()
immediately returnsTrue
The function returns True
because the substring "ab"
appears in both the original and reversed string.
Let's also trace through a negative example with s = "abcd"
:
Step 1: Reverse and extract substrings
- Original:
"abcd"
- Reversed:
"dcba"
- Pairs from reversed:
('d','c')
,('c','b')
,('b','a')
- Set
st = {('d','c'), ('c','b'), ('b','a')}
Step 2: Check original string's substrings
- Pairs from original:
('a','b')
→ Not inst
✗('b','c')
→ Not inst
✗('c','d')
→ Not inst
✗
- No matches found, return
False
The key insight is that we're looking for exact 2-character matches, and the set-based approach lets us check this efficiently in linear time.
Solution Implementation
1from itertools import pairwise
2
3class Solution:
4 def isSubstringPresent(self, s: str) -> bool:
5 """
6 Check if any 2-character substring of s also appears in the reverse of s.
7
8 Args:
9 s: Input string to check
10
11 Returns:
12 True if a common 2-character substring exists, False otherwise
13 """
14 # Create a set of all consecutive character pairs from the reversed string
15 # Each pair is stored as a tuple (first_char, second_char)
16 reversed_pairs = {(first_char, second_char)
17 for first_char, second_char in pairwise(s[::-1])}
18
19 # Check if any consecutive character pair from the original string
20 # exists in the set of reversed string pairs
21 return any((first_char, second_char) in reversed_pairs
22 for first_char, second_char in pairwise(s))
23
1class Solution {
2 public boolean isSubstringPresent(String s) {
3 // Create a 2D array to track if a character pair exists
4 // reversedPairs[i][j] = true means the pair (char(j), char(i)) exists in the string
5 boolean[][] reversedPairs = new boolean[26][26];
6 int stringLength = s.length();
7
8 // First pass: Record all consecutive character pairs in reverse order
9 // For each pair (s[i], s[i+1]), mark reversedPairs[s[i+1]][s[i]] as true
10 for (int i = 0; i < stringLength - 1; ++i) {
11 int currentChar = s.charAt(i) - 'a';
12 int nextChar = s.charAt(i + 1) - 'a';
13 reversedPairs[nextChar][currentChar] = true;
14 }
15
16 // Second pass: Check if any consecutive pair exists in its forward form
17 // If we find a pair (s[i], s[i+1]) that was already recorded in reverse,
18 // it means this 2-character substring appears both forward and backward
19 for (int i = 0; i < stringLength - 1; ++i) {
20 int currentChar = s.charAt(i) - 'a';
21 int nextChar = s.charAt(i + 1) - 'a';
22 if (reversedPairs[currentChar][nextChar]) {
23 return true;
24 }
25 }
26
27 return false;
28 }
29}
30
1class Solution {
2public:
3 bool isSubstringPresent(string s) {
4 // Create a 2D boolean array to store whether a character pair exists
5 // reversedPairs[i][j] = true means the substring where character 'a'+j
6 // is followed by character 'a'+i exists in the string
7 bool reversedPairs[26][26]{};
8
9 int stringLength = s.size();
10
11 // First pass: Mark all consecutive character pairs from the string
12 // Store them in reversed order (second char as first index, first char as second index)
13 for (int i = 0; i < stringLength - 1; ++i) {
14 int firstChar = s[i] - 'a';
15 int secondChar = s[i + 1] - 'a';
16 reversedPairs[secondChar][firstChar] = true;
17 }
18
19 // Second pass: Check if any consecutive pair exists in its reversed form
20 // If we find a pair (a,b) and previously stored (b,a), return true
21 for (int i = 0; i < stringLength - 1; ++i) {
22 int firstChar = s[i] - 'a';
23 int secondChar = s[i + 1] - 'a';
24 if (reversedPairs[firstChar][secondChar]) {
25 return true;
26 }
27 }
28
29 return false;
30 }
31};
32
1/**
2 * Checks if any 2-character substring appears both in the string and its reverse
3 * @param s - Input string containing only lowercase letters
4 * @returns true if a 2-character substring exists in both s and reverse(s), false otherwise
5 */
6function isSubstringPresent(s: string): boolean {
7 // Create a 26x26 matrix to track all 2-character substrings
8 // adjacencyMatrix[i][j] = true means character i followed by character j exists in the string
9 const adjacencyMatrix: boolean[][] = Array.from(
10 { length: 26 },
11 () => Array(26).fill(false)
12 );
13
14 // Mark all 2-character substrings present in the original string
15 // Note: We're marking in reverse order (second char -> first char)
16 // This effectively stores the reversed substrings
17 for (let i = 0; i < s.length - 1; i++) {
18 const firstCharIndex = s.charCodeAt(i) - 97; // Convert 'a'-'z' to 0-25
19 const secondCharIndex = s.charCodeAt(i + 1) - 97; // Convert 'a'-'z' to 0-25
20 adjacencyMatrix[secondCharIndex][firstCharIndex] = true;
21 }
22
23 // Check if any 2-character substring from the original string
24 // exists in the reversed form (already stored in the matrix)
25 for (let i = 0; i < s.length - 1; i++) {
26 const firstCharIndex = s.charCodeAt(i) - 97; // Convert 'a'-'z' to 0-25
27 const secondCharIndex = s.charCodeAt(i + 1) - 97; // Convert 'a'-'z' to 0-25
28
29 // If the substring exists in both original and reversed form
30 if (adjacencyMatrix[firstCharIndex][secondCharIndex]) {
31 return true;
32 }
33 }
34
35 return false;
36}
37
Time and Space Complexity
Time Complexity: O(n)
The algorithm consists of two main operations:
- Creating a set
st
from consecutive pairs in the reversed strings[::-1]
, which takesO(n)
time to reverse the string andO(n)
time to iterate through all consecutive pairs. - Checking if any consecutive pair from the original string exists in the set, which takes
O(n)
time to iterate through pairs andO(1)
average time for each set lookup.
Therefore, the overall time complexity is O(n)
where n
is the length of the string.
Space Complexity: O(|Σ|²)
The space is primarily consumed by the set st
which stores tuples of consecutive character pairs. In the worst case, the set can contain all possible combinations of two characters from the alphabet. Since the string consists of lowercase English letters where |Σ| = 26
, the maximum number of possible pairs is 26 × 26 = 676
. This gives us a space complexity of O(|Σ|²)
which is O(26²) = O(676) = O(1)
in terms of constant space, but more precisely expressed as O(|Σ|²)
to show its dependency on the alphabet size.
The reversed string s[::-1]
also takes O(n)
space temporarily, but since n
can be arbitrarily large while |Σ|²
is bounded by the alphabet size, the dominant factor in the worst case is O(min(n, |Σ|²))
, which simplifies to O(|Σ|²)
as stated in the reference answer.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Not Handling Edge Cases for Short Strings
The pairwise()
function returns an empty iterator when the input string has length less than 2. While the current solution handles this correctly (returning False
for empty iterations), developers might incorrectly assume they need special handling for these cases.
Pitfall Example:
def isSubstringPresent(self, s: str) -> bool:
# Unnecessary edge case handling
if len(s) < 2:
return False # This check is redundant
reversed_pairs = {(a, b) for a, b in pairwise(s[::-1])}
return any((a, b) in reversed_pairs for a, b in pairwise(s))
Why it's unnecessary: The pairwise()
function naturally handles strings of length 0 or 1 by returning no pairs, causing both the set comprehension and any()
to work correctly without explicit checks.
2. Comparing Strings Instead of Character Tuples
A common mistake is trying to store and compare 2-character substrings as strings instead of tuples, which can lead to subtle bugs or inefficiencies.
Pitfall Example:
def isSubstringPresent(self, s: str) -> bool:
# Using string concatenation instead of tuples
reversed_pairs = {a + b for a, b in pairwise(s[::-1])}
return any(a + b in reversed_pairs for a, b in pairwise(s))
Issue: While this works, string concatenation is less efficient than tuple creation and comparison. Tuples are immutable and have better hash performance.
3. Manually Implementing Sliding Window Instead of Using pairwise()
Without knowledge of itertools.pairwise()
(introduced in Python 3.10), developers might write error-prone manual implementations.
Pitfall Example:
def isSubstringPresent(self, s: str) -> bool:
reversed_s = s[::-1]
reversed_pairs = set()
# Manual sliding window - prone to off-by-one errors
for i in range(len(reversed_s) - 1): # Easy to forget the -1
reversed_pairs.add((reversed_s[i], reversed_s[i + 1]))
for i in range(len(s) - 1):
if (s[i], s[i + 1]) in reversed_pairs:
return True
return False
Better Alternative for Python < 3.10:
def isSubstringPresent(self, s: str) -> bool:
# Define pairwise locally if not available
def pairwise(iterable):
a = iter(iterable)
b = iter(iterable)
next(b, None)
return zip(a, b)
reversed_pairs = {(a, b) for a, b in pairwise(s[::-1])}
return any((a, b) in reversed_pairs for a, b in pairwise(s))
4. Incorrect Understanding of the Problem
Some might misinterpret the problem as finding palindromic substrings or checking if the same 2-character substring appears twice in the original string.
Pitfall Example:
def isSubstringPresent(self, s: str) -> bool:
# Wrong: Checking for palindromic substrings
for i in range(len(s) - 1):
if s[i] == s[i + 1]: # This checks for repeated characters
return True
return False
Correct Understanding: We need to check if any 2-character substring from s
appears anywhere in reverse(s)
, not just at the corresponding reversed position.
How many times is a tree node visited in a depth first search?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!