Comprehensive coverage of language level operations and algorithmic techniques for arrays and strings that are commonly evaluated in coding interviews. Candidates should understand common language methods for arrays and strings, including their parameters and return values, chaining of operations, and the implications of mutable versus immutable types for in place versus extra space solutions. Core algorithmic patterns include iteration and traversal, index based and pointer based approaches, two pointer strategies, sliding window, prefix and suffix sums, sorting and partitioning, and cumulative or running sums. Problem classes include traversal, insertion and deletion, reversing and rotating, merging and deduplicating, subarray and substring search, anagram detection, palindrome detection, longest substring and maximum subarray problems, and pointer based reordering and partitioning tasks. Pattern matching techniques include naive matching, Knuth Morris Pratt and rolling hash approaches, and hashing for frequency and membership checks. String transformation and comparison topics include edit distance, sequence transformation problems such as word ladder, and parsing and validation tasks. Candidates should be prepared to implement correct and efficient solutions in common programming languages, reason about time and space complexity, optimize for input size and memory constraints, handle edge cases such as empty inputs and boundary conditions, and address character level concerns such as encoding differences, multibyte characters, surrogate pairs and unicode normalization. Interviewers may probe language specific implementation details, in place mutation versus copying, fixed buffer strategies, streaming or incremental algorithms for large inputs, and trade offs between clarity and performance. Expect questions that require selecting the right algorithmic pattern, implementing a robust solution, and justifying complexity and memory decisions.
HardTechnical
64 practiced
Describe algorithms to compute the number of distinct substrings of a string. Provide the O(n^2) naive approach and then explain how suffix arrays or suffix automata achieve O(n) or O(n log n) solutions. Outline how you'd implement and test the suffix-array-based approach for large inputs.
Sample Answer
**Brief approaches**- Naive O(n^2): enumerate all start i, end j substrings, insert into a hash set (or rolling-hash to avoid O(k) per compare). Time O(n^2) substrings × average O(1) hash = O(n^2) time, O(n^2) memory worst-case.- Suffix array O(n log n): build suffix array SA and LCP array. Number of distinct substrings = sum_{i=0..n-1} (n - SA[i]) - LCP[i] where LCP[0]=0. SA via doubling method is O(n log n); Kasai computes LCP in O(n).- Suffix automaton O(n): construct SAM online in O(n) (states ~2n). Each new character contributes new substrings = length[cur] - length[link[cur]]. Sum over extensions gives answer.**Why these work (intuition)**- SA+LCP: each suffix contributes all its prefixes; LCP removes prefixes already seen in previous suffix.- SAM: states compactly represent end-pos equivalence classes; difference in lengths counts new distinct substrings.**Suffix-array-based implementation outline (for large inputs)**- Use an efficient SA algorithm (SA-IS for linear or doubling with radix sort for O(n log n)). Use integer alphabets (map chars to ints).- Compute LCP with Kasai (O(n), low memory).- Use 64-bit counters for totals.- Memory/IO: stream input, avoid extra copies; store SA, rank, LCP as int arrays.- Handle Unicode by normalizing to UTF-8 codepoints.Sample high-level pseudocode:
python
# build SA (doubling with radix sort), build LCP (Kasai)
distinct = sum((n - SA[i]) - LCP[i] for i in range(n))
**Testing for large inputs**- Unit tests: small strings with known answers ("aaa"→3, "ababa"→9).- Property tests: concat random string S + unique delimiter + S; distinct substrings should be > substrings of S.- Stress tests: random and worst-case (repeated 'a' * 1e6) to verify time/memory and 64-bit overflow.- Benchmarks: measure runtime and memory; compare to SAM implementation for sanity.As a full-stack developer, I’d wrap the core algorithm as a backend service (streaming input, chunked processing), expose endpoints for analysis, and add client-side progress reporting for large jobs.
EasyTechnical
46 practiced
Write a JavaScript function to determine whether two strings are anagrams of each other. Consider case-insensitive comparison and assume Unicode input. Return true if they are anagrams (same character counts), false otherwise. Discuss time and space complexity and how to handle large input efficiently.
Sample Answer
**Approach (brief)** Normalize both strings for Unicode (NFC) and case-insensitive compare, then compare character frequency maps using Unicode code points (iterate with for...of).**Code**
javascript
// Returns true if s1 and s2 are anagrams (Unicode-aware, case-insensitive)
function areAnagrams(s1, s2) {
// Normalize and case-fold (toLowerCase is best available in JS; consider locale if needed)
const a = s1.normalize('NFC').toLowerCase();
const b = s2.normalize('NFC').toLowerCase();
// Quick length check in code points (iterate to count without creating arrays)
let lenA = 0, lenB = 0;
for (const _ of a) lenA++;
for (const _ of b) lenB++;
if (lenA !== lenB) return false;
// Build frequency map for a, decrement with b
const freq = new Map();
for (const ch of a) freq.set(ch, (freq.get(ch) || 0) + 1);
for (const ch of b) {
const c = freq.get(ch);
if (!c) return false;
if (c === 1) freq.delete(ch); else freq.set(ch, c - 1);
}
return freq.size === 0;
}
**Why this works / key details**- normalize('NFC') makes combining marks consistent.- for...of iterates Unicode code points (handles surrogate pairs).- Map handles arbitrary Unicode characters.**Complexity**- Time: O(n) where n = total code points (single pass builds + single pass compare).- Space: O(k) where k = unique characters (Map).**Handling large input**- Early length check avoids extra work when lengths differ.- Single-pass counting minimizes scans.- For very large strings, process as streams/chunks on the server to avoid holding entire strings in memory and merge frequency counts.
HardTechnical
66 practiced
Implement a function in Python that reorders a string so that no two adjacent characters are the same. If it's impossible, return an empty string. Aim for an approach using a max-heap (priority queue) of character counts and greedy selection. Example: 'aaabbc' -> 'ababac' (any valid reorder). Analyze time/space complexity.
Sample Answer
**Approach (brief)** Use a max-heap of (count, char). Greedily pop the two most frequent chars and append them (re-push with decremented counts). This prevents adjacent duplicates. If one char remains, verify its count is 1.
python
from collections import Counter
import heapq
def reorganize_string(s: str) -> str:
# Count frequencies
counts = Counter(s)
# Max-heap in Python using negative counts
heap = [(-cnt, ch) for ch, cnt in counts.items()]
heapq.heapify(heap)
res = []
# Greedy: always take top two different chars
while len(heap) >= 2:
cnt1, ch1 = heapq.heappop(heap)
cnt2, ch2 = heapq.heappop(heap)
res.extend([ch1, ch2])
# decrement and push back if still available
if cnt1 + 1 < 0:
heapq.heappush(heap, (cnt1 + 1, ch1))
if cnt2 + 1 < 0:
heapq.heappush(heap, (cnt2 + 1, ch2))
# If one remains, it can be placed only if count == 1
if heap:
cnt, ch = heapq.heappop(heap)
if cnt == -1:
res.append(ch)
else:
return "" # impossible
return "".join(res)
**Key reasoning & correctness** - Always pairing the two highest-frequency chars reduces chance of future conflicts; it's a classic greedy proof by exchange. - If any char's count > (n+1)//2 a valid reorder is impossible; the heap process detects that when a leftover with count >1 remains.**Complexity** - Time: O(n log k) where n = |s| and k = distinct chars (heap ops per character). - Space: O(k + n) for heap and result.**Edge cases** - Empty string -> "". - Single character -> itself. - All same char repeated -> "" when length >1.
MediumTechnical
44 practiced
Implement a JavaScript function to validate whether a given string is a valid IPv4 or IPv6 address. For IPv4, each octet should be 0-255 with no leading zeros unless the octet is zero; for IPv6, validate eight groups of 1-4 hex digits, allowing shorthand '::' once. Discuss edge cases and complexity.
Sample Answer
**Approach (brief)** I implement two validators: IPv4 by splitting on '.', checking 4 octets, numeric range 0–255 and no leading zeros; IPv6 by handling at most one '::', expanding shorthand, and validating eight groups of 1–4 hex digits.**Code**
javascript
// Returns "IPv4", "IPv6" or "Neither"
function validateIP(s) {
if (s.includes('.')) return isIPv4(s) ? "IPv4" : "Neither";
if (s.includes(':')) return isIPv6(s) ? "IPv6" : "Neither";
return "Neither";
}
function isIPv4(s) {
const parts = s.split('.');
if (parts.length !== 4) return false;
for (const p of parts) {
if (p.length === 0) return false;
if (p[0] === '0' && p.length > 1) return false; // leading zero
if (!/^\d+$/.test(p)) return false;
const n = Number(p);
if (n < 0 || n > 255) return false;
}
return true;
}
function isIPv6(s) {
if (s === '::') return true;
const parts = s.split('::');
if (parts.length > 2) return false; // more than one '::'
const left = parts[0] ? parts[0].split(':').filter(Boolean) : [];
const right = parts[1] ? parts[1].split(':').filter(Boolean) : [];
if (left.length + right.length > 8) return false;
const groups = [...left, ...right];
if (groups.length === 0) return false;
for (const g of groups) {
if (!/^[0-9a-fA-F]{1,4}$/.test(g)) return false;
}
return left.length + right.length <= 8;
}
**Key concepts & edge cases**- IPv4: reject leading zeros ("01"), allow "0". Reject non-numeric and out-of-range values.- IPv6: allow single '::' shorthand (including leading or trailing), exactly 8 groups after expansion. Accept uppercase/lowercase hex.- Special cases: "192.168.1", "256.1.1.1", "1::1::1", "::", "0:0:0:0:0:0:0:0".**Complexity**- Time: O(n) where n = length of string (single pass splits and regex checks).- Space: O(n) for split arrays.I’d add unit tests covering the edge cases above and integrate this into server-side input validation and client-side form checks for quicker feedback.
EasyTechnical
64 practiced
Given a sentence as a string, implement a function in Python that reverses the order of words. For input 'the sky is blue' return 'blue is sky the'. Trim extra spaces so multiple spaces between words become single spaces in output, and remove leading/trailing spaces. Discuss in-place vs new-string approaches.
Sample Answer
**Approach (brief)**Trim spaces, split words, reverse order, join with single spaces. For in-place (char-array) you can reverse whole array then reverse each word — useful when memory is constrained (e.g., embedded). For Python/full-stack work, new-string approach is simpler and idiomatic.**Python implementation (clean, new-string)**
python
def reverse_words(s: str) -> str:
# split() trims and collapses multiple spaces by default
words = s.split()
# reverse list and join with single space
return " ".join(reversed(words))
# Example:
# reverse_words(" the sky is blue ") -> "blue is sky the"
**In-place idea (char array)**- Convert to list(chars), reverse entire list, then scan and reverse each word in-place, and finally compact spaces.- Pros: O(1) extra space (ignoring input->list conversion). Cons: more complex, error-prone in high-level languages; rarely needed in practice for web apps.**Complexity**- New-string: O(n) time, O(n) extra space.- In-place: O(n) time, O(1) extra space (after converting to mutable array).**Edge cases**- Empty string -> ""- All spaces -> ""- Single word -> same wordThis solution is idiomatic for Python backend/frontend utilities and easy to maintain in full-stack projects.
Unlock Full Question Bank
Get access to hundreds of Array and String Manipulation interview questions and detailed answers.