Strings
Key idea — String problems almost always reduce to one of: frequency comparison (anagram, permutation), sliding window on characters, or index/pointer manipulation. A frequency map (map[byte]int or a fixed [26]int array) is the go-to supporting structure.
When to use — Anagram detection, longest substring without repeating characters, minimum window substring, palindrome checks, string matching, word frequency.
Pattern / template
go
// isAnagram checks whether s and t are anagrams of each other.
func isAnagram(s, t string) bool {
if len(s) != len(t) {
return false
}
freq := [26]int{}
for i := 0; i < len(s); i++ {
freq[s[i]-'a']++
freq[t[i]-'a']--
}
return freq == [26]int{}
}
// lengthOfLongestSubstring returns the length of the longest substring
// without repeating characters.
func lengthOfLongestSubstring(s string) int {
charIndex := map[byte]int{}
left, best := 0, 0
for right := 0; right < len(s); right++ {
ch := s[right]
if idx, ok := charIndex[ch]; ok && idx >= left {
left = idx + 1
}
charIndex[ch] = right
if size := right - left + 1; size > best {
best = size
}
}
return best
}
// findAnagrams returns all start indices where an anagram of p begins in s.
func findAnagrams(s, p string) []int {
if len(p) > len(s) {
return nil
}
var need, window [26]int
for i := 0; i < len(p); i++ {
need[p[i]-'a']++
window[s[i]-'a']++
}
result := []int{}
if window == need {
result = append(result, 0)
}
for i := len(p); i < len(s); i++ {
window[s[i]-'a']++
window[s[i-len(p)]-'a']--
if window == need {
result = append(result, i-len(p)+1)
}
}
return result
}Complexity
- Frequency map build: O(n).
- Sliding window: O(n) time, O(alphabet size) space.
- Counter comparison: O(alphabet size) — effectively O(1) for fixed alphabets.
Pitfalls
- Comparing
[26]intarrays is O(1) in Go; for large/arbitrary alphabets use amap[rune]intwith a running match count instead. - In Go, strings are immutable — building a result with
+=in a loop is O(n²); use astrings.Builderor[]bytebuffer instead. - Sliding window on strings: shrink the window only when the invariant is violated, not on every step.
Common follow-ups
- How would you find all anagram occurrences of a pattern in a string in O(n)?
- What is the time complexity of the naive palindrome check, and how does Manacher's algorithm improve it?
- How do you handle Unicode or multi-byte characters when using character frequency maps?
- When is a Rabin-Karp rolling hash useful compared to a sliding window frequency map?
Practice (LeetCode)
- LC 3 — Longest Substring Without Repeating Characters
- LC 5 — Longest Palindromic Substring
- LC 76 — Minimum Window Substring
- LC 242 — Valid Anagram
- LC 14 — Longest Common Prefix