Article Categories
- All Categories
-
Data Structure
-
Networking
-
RDBMS
-
Operating System
-
Java
-
MS Excel
-
iOS
-
HTML
-
CSS
-
Android
-
Python
-
C Programming
-
C++
-
C#
-
MongoDB
-
MySQL
-
Javascript
-
PHP
-
Economics & Finance
Program to find start indices of all anagrams of a string S in T in Python
Suppose we have two strings S and T, we have to find all the start indices of S's anagrams in T. The strings consist of lowercase letters only and the length of both strings S and T will not be larger than 20 and 100.
So, if the input is like S = "cab" T = "bcabxabc", then the output will be [0, 1, 5] as the substrings "bca", "cab" and "abc" are anagrams of "cab".
Using Sliding Window with Character Count
We can solve this using a sliding window approach with character frequency counting ?
def find_anagrams(s, p):
from collections import Counter
if len(p) > len(s):
return []
p_count = Counter(p)
window_count = Counter()
result = []
# Initialize the window
for i in range(len(p)):
window_count[s[i]] += 1
# Check if first window is anagram
if window_count == p_count:
result.append(0)
# Slide the window
for i in range(len(p), len(s)):
# Add new character
window_count[s[i]] += 1
# Remove old character
left_char = s[i - len(p)]
window_count[left_char] -= 1
if window_count[left_char] == 0:
del window_count[left_char]
# Check if current window is anagram
if window_count == p_count:
result.append(i - len(p) + 1)
return result
# Test the function
s = "bcabxabc"
p = "cab"
print(find_anagrams(s, p))
[0, 1, 5]
Using Dictionary-based Approach
Here's another approach using dictionary to track character frequencies ?
def find_anagrams_dict(s, p):
if len(p) > len(s):
return []
# Count characters in pattern
p_dict = {}
for char in p:
p_dict[char] = p_dict.get(char, 0) + 1
result = []
window_size = len(p)
# Check each window of size len(p)
for i in range(len(s) - len(p) + 1):
window = s[i:i + window_size]
# Count characters in current window
window_dict = {}
for char in window:
window_dict[char] = window_dict.get(char, 0) + 1
# Compare dictionaries
if window_dict == p_dict:
result.append(i)
return result
# Test the function
s = "bcabxabc"
p = "cab"
print(find_anagrams_dict(s, p))
[0, 1, 5]
Optimized Sliding Window
This optimized version maintains a match counter to avoid comparing entire dictionaries ?
def find_anagrams_optimized(s, p):
if len(p) > len(s):
return []
# Character frequency map for pattern
p_count = {}
for char in p:
p_count[char] = p_count.get(char, 0) + 1
result = []
left = 0
matches = 0
window_count = {}
for right in range(len(s)):
# Add character from right
right_char = s[right]
if right_char not in window_count:
window_count[right_char] = 0
window_count[right_char] += 1
if right_char in p_count and window_count[right_char] == p_count[right_char]:
matches += 1
# Remove character from left if window is too large
if right >= len(p):
left_char = s[left]
if left_char in p_count and window_count[left_char] == p_count[left_char]:
matches -= 1
window_count[left_char] -= 1
left += 1
# Check if we found an anagram
if matches == len(p_count):
result.append(left)
return result
# Test the function
s = "bcabxabc"
p = "cab"
print(find_anagrams_optimized(s, p))
[0, 1, 5]
Comparison
| Method | Time Complexity | Space Complexity | Best For |
|---|---|---|---|
| Counter Approach | O(n) | O(k) | Clean, readable code |
| Dictionary Approach | O(n * k) | O(k) | Simple implementation |
| Optimized Sliding Window | O(n) | O(k) | Maximum efficiency |
Where n is the length of string s and k is the length of pattern p.
Conclusion
The sliding window approach with Counter provides the best balance of efficiency and readability. Use the optimized version when performance is critical, and the dictionary approach for educational purposes to understand the core logic.
