Anagram Index Finder — Problem Statement & Solution Guide

Sliding WindowMediumSliding Window / Hash Map
TimeO(n)
|
SpaceO(1)

Quick Answer & Algorithm Key Takeaway

Master coding challenges related to Sliding Window and solve the Anagram Index Finder problem optimally.

TopicSliding Window
PatternSliding Window / Hash Map
TimeO(n)
SpaceO(1)

Problem Description

You are given two strings, sequence and pattern. Return a list of all starting indices i such that the substring sequence[i..i+|pattern|-1] is a permutation of pattern. The order of indices in the output does not matter. The solution must run in linear time relative to the length of sequence and use only O(1) additional space besides the output.

DSA Pattern Breakdown

DSA Pattern Breakdown

"Anagram Index Finder"

medium

WHY DOES IT MATTER?

Detecting anagrams in a stream is a classic example of pattern matching where order does not matter, a scenario that appears in security (signature detection), bioinformatics (motif search), and text analytics. Mastering this pattern shows you can transform a combinatorial check into a constant‑time update, a skill valued in performance‑critical code.

OPTIMIZATION CHALLENGE

The key insight is that the equality of two multisets can be tracked by a single mismatch counter instead of comparing whole frequency arrays each slide. When the counter hits zero, the window is an anagram.

REAL-WORLD CONNECTION

Think of a conveyor belt where each item carries a set of attributes. A quality sensor only needs to know the difference between the current batch's attribute histogram and the target histogram; as the belt moves, the sensor drops the leftmost item and adds the new one, keeping the check O(1).

During an interview, initialize the frequency diff array with the pattern counts, then slide the window while updating the diff and the mismatch counter; this avoids a second full‑array comparison and keeps the code clean.

COMPLEXITY AT A GLANCE

⏱ Time:O(n)
💾 Space:O(1)

Core Theory — Why This Approach?

The problem asks for all start positions where a sliding window of length |pattern| in the sequence is an anagram of the pattern. A naive solution would compare the sorted window or a frequency map for each possible start, leading to O(n·m) time where n is the length of the sequence and m is the length of the pattern. This quickly becomes prohibitive for large inputs because each window is recomputed from scratch. The optimal paradigm leverages the sliding‑window technique combined with a fixed‑size character count array (or hashmap) that can be updated in O(1) as the window moves one step to the right: decrement the count of the character leaving the window and increment the count of the character entering. By also maintaining a mismatch counter that tracks how many character frequencies differ from the pattern, we can decide in constant time whether the current window is a valid permutation. This yields a linear‑time O(n) algorithm with O(1) auxiliary space (assuming a constant alphabet size).

Interview Questions on This Problem

Q1How would you modify the sliding‑window solution if the input strings could contain Unicode characters beyond the ASCII range?

Use a hashmap (e.g., unordered_map<char32_t,int>) instead of a fixed‑size array to store frequencies; the rest of the algorithm stays the same, still O(n) time but O(k) space where k is the number of distinct characters in the pattern.

Q2In a distributed log‑processing system, you need to detect anagrammatic signatures in a massive stream. Which property of the sliding‑window algorithm makes it suitable for such a setting?

The algorithm processes the stream in a single pass and updates constant‑size state per new character, enabling it to run on a streaming architecture with bounded memory and without needing to store the entire log.

Q3Why is it safe to stop the scan once the remaining characters are fewer than the pattern length, and how would you express that guard in code?

If the remaining length < |pattern|, no further full‑size windows exist, so the loop can terminate early; in code you typically iterate i from 0 to n‑m inclusive (for i<=n-m).

Examples

Example 1

Input

{ "sequence": "abdcbaacb", "pattern": "abc" }

Output

[0,4,5,6]

Explanation: The pattern length is 3. Substrings of length 3 are: "abd" (not an anagram), "bdc" (no), "dcb" (no), "cba" (anagram, start 3), "baa" (no), "aac" (no), "acb" (anagram, start 6). Additionally, the substring "abc" appears at index 0 and "bca" at index 4, giving indices 0,4,5,6.

Example 2

Input

{ "sequence": "zzxyzzxyz", "pattern": "zxy" }

Output

[2,5]

Explanation: Pattern length is 3. Sliding window yields substrings: "zzx" (no), "zxy" (anagram at index 2), "xyz" (anagram at index 3), "yzz" (no), "zzx" (no), "zxy" (anagram at index 5). The distinct start positions are 2 and 5.

Example 3

Input

{ "sequence": "aaaaa", "pattern": "aa" }

Output

[0,1,2,3]

Explanation: Every window of length 2 consists of two 'a' characters, which matches the pattern's character multiset. Hence start indices 0,1,2,3 are returned.

Constraints

  • 1 <= sequence.length <= 10^5
  • 1 <= pattern.length <= 10^5
  • pattern.length <= sequence.length
  • sequence and pattern contain only lowercase English letters

Optimal Approach & Strategy

Maintain a rolling frequency map and a mismatch counter while sliding the window one character at a time – O(n) time, O(1) extra space.

Brute Force Approach

Check every possible substring of length |pattern|, sort it or count frequencies, and compare to the pattern – O(n·m) time.

Code Solutions

JavaScript Solution
Time: O(n)
const fs = require('fs');
const input = fs.readFileSync(0,'utf8').trim().split(/\s+/);
function findAnagramIndices(sequence, pattern){
    const n = sequence.length, m = pattern.length;
    if(m===0 || n<m) return [];
    const ALPH = 256;
    const need = new Array(ALPH).fill(0);
    const have = new Array(ALPH).fill(0);
    for(let i=0;i<m;i++) need[pattern.charCodeAt(i)]++;
    let diff = 0;
    for(let i=0;i<ALPH;i++) if(need[i]!==0) diff++;
    const res = [];
    for(let i=0;i<n;i++){
        const inChar = sequence.charCodeAt(i);
        have[inChar]++;
        if(have[inChar]===need[inChar]) diff--; else if(have[inChar]===need[inChar]+1) diff++;
        if(i>=m){
            const outChar = sequence.charCodeAt(i-m);
            if(have[outChar]===need[outChar]) diff++; else if(have[outChar]===need[outChar]+1) diff--;
            have[outChar]--;
        }
        if(i>=m-1 && diff===0) res.push(i-m+1);
    }
    return res;
}
if(input.length>=2){
    const seq = input[0];
    const pat = input[1];
    const ans = findAnagramIndices(seq, pat);
    console.log(ans.join(' '));
}

Asked in Top Tech Interviews

Salesforce

Solve in Interative Editor

Ready to test your code? Open our built-in compiler, run custom test suites, and see detailed complexity analysis reports instantly.