Problem statement

Given a string s and a dictionary of strings wordDict, return true if s can be segmented into a space-separated sequence of one or more dictionary words.

Note that the same word in the dictionary may be reused multiple times in the segmentation.
Link to problem

Example 1

Input: s = "leetcode", wordDict = ["leet","code"]
Output: true
Explanation: Return true because "leetcode" can be segmented as "leet code".

Example 2

Input: s = "applepenapple", wordDict = ["apple","pen"]
Output: true
Explanation: Return true because "applepenapple" can be segmented as "apple pen apple".
Note that you are allowed to reuse a dictionary word.

Solutions

Approach 1: Dynamic Programming

The gist of dynamic programming in this problem is trying to move a pointer forward in a.. decreasing fashion. The best way to explain this is through an example.

Imagine you are given "leetcode" and a word dict ["leet", "code"]

What you’re trying to do is this:

l

le 
 e

lee
 ee
  e

leet - match found

leetc
 eetc
  etc
   tc
    c

...

leetcode
 eetcode
  etcode
   tcode
    code - match found

Implementation in Rust

fn word_break(s: String, word_dict: Vec<String>) -> bool {
    let mut breakable = vec![false; s.len() + 1];
    breakable[0] = true;

    for end in 0..s.len() + 1 {
        for start in 0..end {
            let word = s[start..end].to_string();
            if breakable[start] && word_dict.contains(&word) {
                breakable[end] = true;
            }
        }
    }

    breakable[s.len()]
}

Time Complexity: $O(nm)$
$n$ is the length of string
$m$ is the length of word dict
Space Complexity: $O(n)$
Runtime on LeetCode: $3$ms

Other implemntations involve BFS/DFS which are simpler in concept but were quite difficulty for me to wrap my head around.