Return true if the string s contains any substring that is an anagram of
part (a contiguous block of len(part) characters with the same letter counts).
Assume lowercase letters only.
hasSubstringAnagram(s, part)
Input format: s | part.
s="cbaebabacd", part="abc" // → true ("cba" and "bac")
s="hello", part="xyz" // → false
s="aa", part="aaa" // → false (part longer than s)
An anagram of part is any rearrangement of its letters — so a substring of s is an anagram exactly when it has the same length and the same letters the same number of times, regardless of order. That reframes the problem: instead of permutations, you only need to compare letter counts.
A natural first idea: for every substring of length len(part), sort its characters and compare to sorted part. Sorting each window costs O(k log k) for k = len(part), across roughly n positions — O(n · k log k). Worse, most characters in one window are still present in the next; sliding by one only swaps a single character, but a full re-sort throws that shared structure away.
Since only order-independent frequency matters, track how many of each letter the current window contains, and compare to part's letter-count profile. Comparing two frequency tables (fixed size — 26 lowercase letters) is O(1), not O(k log k).
The window has a fixed width of len(part), so the sliding-window trick applies directly: as it moves one step right, one character leaves on the left and one enters on the right. Increment the entering letter's count and decrement the leaving one's, instead of rebuilding the table from scratch.
part once, up front.len(part) across s, maintaining its own frequency table.s without a match, return false.Since you only add or remove one character per step and compare two small fixed-size tables, the whole scan is O(n) with O(1) extra space (bounded by the alphabet, not k or n). This is the string cousin of the fixed sum/product window pattern: consecutive windows overlap almost entirely, so update the summary incrementally instead of recomputing it.
def has_substring_anagram(s, part):
if len(part) > len(s):
return False
need = [0] * 26
win = [0] * 26
for ch in part:
need[ord(ch) - 97] += 1
for i in range(len(s)):
win[ord(s[i]) - 97] += 1
if i >= len(part):
win[ord(s[i - len(part)]) - 97] -= 1
if i >= len(part) - 1 and win == need:
return True
return False
Sign in to join the discussion.
No comments yet. Be the first to ask a question.
Press Run to execute your code, or Submit to test and complete this problem.