Home/Blog/String Manipulation Interview Questions: Two Pointers, Sliding Window & Frequency Counting
stringsDSAcoding interview11 min read

String Manipulation Interview Questions: Core Patterns

String problems dominate FAANG interviews because strings combine multiple patterns — two pointers, sliding window, frequency counting, and hash maps. Mastering these four patterns covers the vast majority of string interview questions.


When to Use Each String Pattern

Two Pointers on Strings:

  • Palindrome problems (valid palindrome, palindrome substrings)
  • String comparison from both ends
  • Reversing words or characters in-place

Sliding Window:

  • Contiguous substring with a constraint (longest substring without repeating characters)
  • Minimum window containing all characters of another string
  • Maximum/minimum substring satisfying a condition

Frequency Counting:

  • Anagram detection and grouping
  • Character frequency comparison
  • Checking if one string is a permutation of another

Pattern Matching:

  • Finding occurrences of one string in another
  • Wildcard or regex matching

The Trigger Pattern

The problem mentions "substring" or "subarray" → sliding window. The problem mentions "anagram" or "permutation" → frequency counting. The problem mentions "palindrome" → two pointers. The problem mentions "group" or "same characters" → frequency count as key.


Frequency Counting: Valid Anagram

Given two strings, determine if one is an anagram of the other. This is the entry-level frequency counting problem.

def is_anagram(s, t):
    if len(s) != len(t):
        return False

    count = {}

    for char in s:
        count[char] = count.get(char, 0) + 1

    for char in t:
        if char not in count:
            return False
        count[char] -= 1
        if count[char] == 0:
            del count[char]

    return len(count) == 0

Why this works: Count every character in the first string, then un-count using the second string. If all counts reach zero, the strings have identical character frequencies. Early return on length mismatch saves time.

Time: O(n). Space: O(1) — at most 26 lowercase letters.

Optimized approach: Use a fixed-size array instead of a dictionary for O(1) space with a known character set.

def is_anagram_optimized(s, t):
    if len(s) != len(t):
        return False

    count = [0] * 26

    for i in range(len(s)):
        count[ord(s[i]) - ord('a')] += 1
        count[ord(t[i]) - ord('a')] -= 1

    return all(c == 0 for c in count)

Frequency Counting: Group Anagrams

Given a list of strings, group anagrams together. This extends the frequency counting pattern to a hash map problem.

from collections import defaultdict

def group_anagrams(strs):
    groups = defaultdict(list)

    for s in strs:
        # Use sorted string as key — anagrams produce identical sorted strings
        key = ''.join(sorted(s))
        groups[key].append(s)

    return list(groups.values())

Why this works: Two strings are anagrams if and only if their sorted versions are identical. Using the sorted string as a hash map key groups all anagrams together. Each group is a list of strings that are anagrams of each other.

Time: O(n × k log k) where n is the number of strings and k is the maximum string length. Space: O(n × k).

Alternative using frequency tuple:

def group_anagrams_freq(strs):
    groups = defaultdict(list)

    for s in strs:
        count = [0] * 26
        for char in s:
            count[ord(char) - ord('a')] += 1
        key = tuple(count)  # Tuple is hashable, list is not
        groups[key].append(s)

    return list(groups.values())

This avoids the O(k log k) sort per string, making it O(n × k) overall.


Sliding Window: Minimum Window Substring

Given strings s and t, find the minimum window in s that contains all characters of t. This is the hardest common sliding window problem.

from collections import Counter

def min_window(s, t):
    if not s or not t or len(s) < len(t):
        return ""

    t_count = Counter(t)
    required = len(t_count)

    # Sliding window pointers
    left = 0
    formed = 0  # Number of unique chars with desired frequency

    window_counts = {}

    ans = (float('inf'), 0, 0)  # (window length, left, right)

    for right in range(len(s)):
        char = s[right]
        window_counts[char] = window_counts.get(char, 0) + 1

        # Check if this char's frequency in window matches desired
        if char in t_count and window_counts[char] == t_count[char]:
            formed += 1

        # Shrink window while it is still valid
        while formed == required:
            # Update answer if this window is smaller
            if right - left + 1 < ans[0]:
                ans = (right - left + 1, left, right)

            # Remove left char from window
            left_char = s[left]
            window_counts[left_char] -= 1
            if left_char in t_count and window_counts[left_char] < t_count[left_char]:
                formed -= 1

            left += 1

    return "" if ans[0] == float('inf') else s[ans[1]:ans[2] + 1]

Why this works: Expand the window rightward until all characters of t are included. Then shrink from the left to find the minimum valid window. The formed counter tracks how many unique characters have reached their required frequency. When formed == required, the window is valid.

Time: O(n). Space: O(k) where k is the number of unique characters in t.


Sliding Window: Longest Substring Without Repeating Characters

Given a string, find the length of the longest substring without repeating characters.

def length_of_longest_substring(s):
    char_index = {}
    max_length = 0
    left = 0

    for right in range(len(s)):
        if s[right] in char_index and char_index[s[right]] >= left:
            # Move left pointer past the duplicate
            left = char_index[s[right]] + 1

        char_index[s[right]] = right
        max_length = max(max_length, right - left + 1)

    return max_length

Why this works: The hash map stores the last seen index of each character. When you see a duplicate, jump the left pointer past its last occurrence. This ensures the window always contains unique characters. You never need to shrink one by one.

Time: O(n). Space: O(min(n, m)) where m is the character set size.


Two Pointers: Valid Palindrome

Given a string, determine if it is a palindrome considering only alphanumeric characters and ignoring cases.

def is_palindrome(s):
    left, right = 0, len(s) - 1

    while left < right:
        # Skip non-alphanumeric characters
        while left < right and not s[left].isalnum():
            left += 1
        while left < right and not s[right].isalnum():
            right -= 1

        if s[left].lower() != s[right].lower():
            return False

        left += 1
        right -= 1

    return True

Why this works: Two pointers start at opposite ends and move inward. Non-alphanumeric characters are skipped. At each valid pair, the characters must match (case-insensitive). If any pair mismatches, the string is not a palindrome.

Time: O(n). Space: O(1).


Two Pointers: Reverse Words in a String

Given a string, reverse the order of words. This demonstrates two pointers for string manipulation.

def reverse_words(s):
    words = s.split()
    left, right = 0, len(words) - 1

    while left < right:
        words[left], words[right] = words[right], words[left]
        left += 1
        right -= 1

    return ' '.join(words)

Why this works: Split the string into words (which handles multiple spaces automatically). Reverse the word array using two pointers. Join with single spaces.

Time: O(n). Space: O(n) for the word array.


Common Mistakes

  1. Forgetting to handle empty strings. Always check for empty input before processing. Many string problems have edge cases with empty strings, single characters, or strings with only whitespace.

  2. Not normalizing case in palindrome problems. Use .lower() for case-insensitive comparison. The problem typically says to ignore case, and forgetting this gives wrong answers.

  3. Using sorted() for anagram problems in production. Sorting works but is O(n log n). Use frequency counting for O(n). In interviews, mention both approaches and explain why frequency counting is optimal.

  4. Off-by-one in sliding window. The window is valid when formed == required, not when the window contains all characters. The difference is in how you count — formed tracks unique characters with the right frequency, not total character count.

  5. Not using Counter from collections. Python's Counter simplifies frequency counting. Do not reinvent it with manual dictionary operations unless asked to implement from scratch.


Practice Problems

Start with these problems to master string manipulation:

  1. Valid Anagram — The entry-level frequency counting problem. Tests basic character counting.
  2. Group Anagrams — Extends frequency counting to grouping. Tests hash map key design.
  3. Minimum Window Substring — The hardest common sliding window problem. Tests expansion and contraction logic.
  4. Longest Substring Without Repeating Characters — The most common sliding window variant. Tests hash map with window tracking.
  5. Valid Palindrome — The entry-level two pointers problem on strings. Tests character skipping logic.

Practice What You Learned

Ready to put this into practice? Try a mock coding interview with an AI interviewer who can give you string manipulation problems and evaluate your approach in real time.


Frequently Asked Questions

When should I use a fixed-size array instead of a hash map for frequency counting?

Use a fixed-size array when the character set is known and small (e.g., 26 lowercase letters). It is O(1) space and faster than a hash map. Use a hash map when the character set is unknown or very large (Unicode). In interviews, mention both approaches and explain your choice.

How do I handle case sensitivity in string problems?

Read the problem carefully. Most problems say "case-insensitive" or "ignore case." Use `.lower()` before comparing. If the problem does not mention case, assume case-sensitive comparison. When in doubt, ask the interviewer.

What's the difference between substring and subsequence?

A substring is contiguous — characters must be adjacent in the original string. A subsequence can skip characters — they just need to maintain relative order. Sliding window applies to substrings. Dynamic programming or backtracking applies to subsequences.

Can I use Python's built-in sort for anagram detection?

Yes, two strings are anagrams if sorted(s) == sorted(t). This is O(n log n). Frequency counting is O(n). In interviews, mention both and explain why frequency counting is optimal. The sorted approach is simpler but less efficient.

How do I optimize the minimum window substring for large inputs?

The O(n) sliding window approach is already optimal. The key optimization is using a hash map with an integer counter (`formed`) instead of comparing frequency maps at each step. This avoids O(k) comparison per window shrink.

Put it into practice

Interview with Alex

Real FAANG-style problems. Instant hiring signal. Free.

Start a Mock Interview →