cswork / libraryOpen practice workspace

Coding questions · Amazon

Determine string segmentability with dictionary

This interview question evaluates algorithm design, data structures, correctness, complexity, edge cases, and implementation details in a realistic interview setting. A strong answer for Determine string segmentability with dictionary states assumptions, handles edge cases, explains trade-offs, and shows how to validate the result clearly.

Given a string s and a dictionary of words, determine whether s can be segmented into a sequence of dictionary words. Provide a dynamic-programming solution and then propose an alternative that uses a Trie to speed up prefix checks and early termination on long inputs. Discuss complexity, memory trade-offs, and how to return one valid segmentation (or all possible segmentations).

What a Strong Answer Covers

  • A clear algorithm with the right data structures and enough pseudocode or code-level detail to implement it.
  • A correctness argument that explains why the algorithm covers all required cases.
  • Time and space complexity, plus at least one alternative approach when relevant.
  • Focused tests for normal cases, edge cases, and failure modes.

Follow-up Questions

  • How would the approach change if the input were streaming or too large for memory?
  • What invariants would you assert in production code?
  • Which tests would catch off-by-one, duplicate, or tie-breaking bugs?

Word Break: Determine String Segmentability with a Dictionary

Given a string s and a list of words word_dict, return true if s can be segmented into a space-separated sequence of one or more words from word_dict. The same dictionary word may be reused any number of times. An empty string is considered segmentable (it is the empty concatenation).

Example 1: s = "leetcode", word_dict = ["leet", "code"] -> true ("leet code"). Example 2: s = "applepenapple", word_dict = ["apple", "pen"] -> true ("apple pen apple"; "apple" is reused). Example 3: s = "catsandog", word_dict = ["cats", "dog", "sand", "and", "cat"] -> false.

The classic O(n^2) approach is bottom-up dynamic programming: dp[i] is true iff the prefix s[:i] can be segmented; dp[i] becomes true when some split point j has dp[j] true and the substring s[j:i] is in the dictionary. An alternative builds a Trie over the dictionary so prefix checks share work and the inner scan can terminate early once no dictionary prefix matches; the Trie keeps the same worst-case time but trades extra memory for fewer redundant substring lookups on long inputs.

Constraints

  • 1 <= len(s) <= 300 (an empty s returns True)
  • 1 <= word_dict.length <= 1000 (an empty dictionary is allowed; only the empty string is then segmentable)
  • 1 <= each word length <= 20
  • s and all dictionary words consist of lowercase English letters
  • Dictionary words may be reused any number of times

Hints

  1. Define dp[i] = True iff the prefix s[:i] can be segmented. dp[0] = True because the empty prefix is trivially segmentable.
  2. For each end index i, look back to a split point j: if dp[j] is True and s[j:i] is a dictionary word, then dp[i] is True.
  3. Put the dictionary in a hash set for O(1) membership, and bound the inner loop by the longest word length so you never test substrings longer than any dictionary word.
  4. For the Trie variant, walk the Trie from each start position; stop the inner scan as soon as no child matches the next character (early termination), which speeds up long inputs with few matching prefixes.

Solution

Solution Alignment

The prompt asks for an implementation-level answer. The safest way to present it is to define the state, maintain clear invariants, then walk through complexity and tests.

Problem Restatement

Given a string s and a dictionary of words, determine whether s can be segmented into a sequence of dictionary words. Provide a dynamic-programming solution and then propose an alternative that uses a Trie to speed up prefix checks and early termination on long inputs. Discuss complexity, memory trade-offs, and how to return one valid segmentation (or all possible segmentations).

Recommended Approach

Use the string constraints to choose between two pointers, a stack, frequency counts, prefix/suffix state, or dynamic programming. Maintain the invariant that processed characters have already been normalized, counted, or matched according to the operation.

Correctness

The implementation should maintain an invariant after each loop or operation that directly matches the problem statement. At termination, that invariant implies the returned value has considered every valid candidate exactly once, or has preserved the required data-structure state after every API call.

Complexity

Most direct string scans are O(n) time. Space ranges from O(1) for two pointers to O(n) for stacks, maps, or DP tables.

Edge Cases and Tests

Empty string, length 1, repeated characters, invalid characters, case sensitivity, Unicode vs ASCII, and very long input.