Open In App

Word Break Problem | (Trie solution)

Last Updated : 05 May, 2025
Summarize
Comments
Improve
Suggest changes
Share
Like Article
Like
Report

Given an input string s and a dictionary dictionary[] containing a set of words, determine whether the string s can be segmented into a sequence of space-separated words that exist in the dictionary.

A string is considered segmentable if it can be broken down into a series of dictionary words, where each segment must match exactly one of the words in the dictionary. The string can be split at any position as long as the resulting substrings are valid words from the dictionary.

This is a famous Google interview question, also being asked by many other companies now a days.

Examples:

Input: s = "ilike", dictionary[] = ["i", "like", "sam", "sung", "samsung", "mobile", "ice", "cream", "icecream", "man", "go", "mango"]
Output: "Yes"
Explanation: The string "ilike" can be segmented into "i like", which consists of valid words "i" and "like" from the dictionary.

Input: s = "ilikesamsung", dictionary[] = ["i", "like", "sam", "sung", "samsung", "mobile", "ice", "cream", "icecream", "man", "go", "mango"]
Output: "Yes"
Explanation: The string can be segmented as "i like samsung" or "i like samsung"

The solution discussed here is mainly an extension of below DP based solution. 
Dynamic Programming | Set 32 (Word Break Problem)

In the above post, a simple array is used to store and search words in a dictionary. Here we use Trie to do these tasks quickly.

A Trie can efficiently store dictionary words and enable fast prefix matching. Each node represents a character, allowing quick checks for the existence of a prefix. By recursively trying all possible prefixes of a string, we can check if each prefix exists in the Trie. If a prefix is found, we then check if the remaining substring is also segmentable. This approach eliminates redundant checks and speeds up the process of determining if the string is segmentable.

C++
#include <iostream>
#include <vector>
using namespace std;

const int ALPHABET_SIZE = 26;

// trie node
struct TNode {
    TNode* child[ALPHABET_SIZE];

    // isEndOfWord is true if the node represents
    // end of a word
    bool isEndOfWord;

    TNode() {
        isEndOfWord = false;
        for (int i = 0; i < ALPHABET_SIZE; i++)
            child[i] = NULL;
    }
};

// If not present, inserts key into trie
// If the key is prefix of trie node, just
// marks leaf node
void insert(TNode* root, string key) {
    TNode* pCrawl = root;

    for (int i = 0; i < key.length(); i++) {
        int index = key[i] - 'a';
        if (!pCrawl->child[index])
            pCrawl->child[index] = new TNode();

        pCrawl = pCrawl->child[index];
    }

    // mark last node as leaf
    pCrawl->isEndOfWord = true;
}

// Returns true if string can be segmented into
// space separated words, otherwise returns false
bool wordBreak(string str, TNode* root) {
    int n = str.size();
    if (n == 0)
        return true;

    vector<bool> dp(n + 1, false);

    // dp[i] is true if str[0..i-1] can be segmented
    dp[0] = true;

    for (int i = 1; i <= n; i++) {
        TNode* curr = root;

        for (int j = i - 1; j >= 0; j--) {
            int index = str[j] - 'a';

            if (!curr->child[index])
                break;

            curr = curr->child[index];

            // If str[j..i-1] is a word and dp[j] is true
            if (curr->isEndOfWord && dp[j]) {
                dp[i] = true;
                break;
            }
        }
    }

    return dp[n];
}

// Driver program to test above functions
int main() {
    string dictionary[] = {
        "mobile", "samsung", "sam", "sung", "ma\n",
        "mango", "icecream", "and", "go", "i",
        "like", "ice", "cream"
    };

    int n = sizeof(dictionary) / sizeof(dictionary[0]);
    TNode* root = new TNode();

    // Construct trie
    for (int i = 0; i < n; i++)
        insert(root, dictionary[i]);

    wordBreak("ilikesamsung", root) ? cout << "Yes\n"
                                    : cout << "No\n";
    wordBreak("iiiiiiii", root) ? cout << "Yes\n"
                                : cout << "No\n";
    wordBreak("", root) ? cout << "Yes\n"
                        : cout << "No\n";
    wordBreak("ilikelikeimangoiii", root) ? cout << "Yes\n"
                                          : cout << "No\n";
    wordBreak("samsungandmango", root) ? cout << "Yes\n"
                                       : cout << "No\n";
    wordBreak("samsungandmangok", root) ? cout << "Yes\n"
                                        : cout << "No\n";

    return 0;
}
Java
// Java implementation of Trie and Word Break
import java.util.*;

class TNode {
    TNode[] child;
    boolean isEndOfWord;

    TNode() {
        child = new TNode[26];
        isEndOfWord = false;
    }
}

public class WordBreak {
    static final int ALPHABET_SIZE = 26;

    // If not present, inserts key into trie
    // If the key is prefix of trie node, just marks leaf node
    static void insert(TNode root, String key) {
        TNode pCrawl = root;

        for (int i = 0; i < key.length(); i++) {
            int index = key.charAt(i) - 'a';
            if (pCrawl.child[index] == null)
                pCrawl.child[index] = new TNode();

            pCrawl = pCrawl.child[index];
        }

        // mark last node as leaf
        pCrawl.isEndOfWord = true;
    }

    // Returns true if string can be segmented into
    // space separated words, otherwise returns false
    static boolean wordBreak(String str, TNode root) {
        int n = str.length();
        if (n == 0)
            return true;

        boolean[] dp = new boolean[n + 1];

        // dp[i] is true if str[0..i-1] can be segmented
        dp[0] = true;

        for (int i = 1; i <= n; i++) {
            TNode curr = root;

            for (int j = i - 1; j >= 0; j--) {
                int index = str.charAt(j) - 'a';

                if (curr.child[index] == null)
                    break;

                curr = curr.child[index];

                // If str[j..i-1] is a word and dp[j] is true
                if (curr.isEndOfWord && dp[j]) {
                    dp[i] = true;
                    break;
                }
            }
        }

        return dp[n];
    }

    public static void main(String[] args) {
        String[] dictionary = {
            "mobile", "samsung", "sam", "sung", "ma",
            "mango", "icecream", "and", "go", "i",
            "like", "ice", "cream"
        };

        TNode root = new TNode();

        // Construct trie
        for (String word : dictionary)
            insert(root, word);

        System.out.println(wordBreak("ilikesamsung", root) ? "Yes" : "No");
        System.out.println(wordBreak("iiiiiiii", root) ? "Yes" : "No");
        System.out.println(wordBreak("", root) ? "Yes" : "No");
        System.out.println(wordBreak("ilikelikeimangoiii", root) ? "Yes" : "No");
        System.out.println(wordBreak("samsungandmango", root) ? "Yes" : "No");
        System.out.println(wordBreak("samsungandmangok", root) ? "Yes" : "No");
    }
}
Python
# Python implementation of Trie and Word Break
class TNode:
    def __init__(self):
        self.child = [None] * 26
        self.isEndOfWord = False

# If not present, inserts key into trie
# If the key is prefix of trie node, just marks leaf node
def insert(root, key):
    pCrawl = root

    for char in key:
        index = ord(char) - ord('a')
        if not pCrawl.child[index]:
            pCrawl.child[index] = TNode()
        pCrawl = pCrawl.child[index]

    # mark last node as leaf
    pCrawl.isEndOfWord = True

# Returns true if string can be segmented into space separated words, otherwise returns false
def wordBreak(s, root):
    n = len(s)
    if n == 0:
        return True

    dp = [False] * (n + 1)
    dp[0] = True  # dp[i] is true if s[0..i-1] can be segmented

    for i in range(1, n + 1):
        curr = root
        for j in range(i - 1, -1, -1):
            index = ord(s[j]) - ord('a')
            if not curr.child[index]:
                break
            curr = curr.child[index]
            # If s[j..i-1] is a word and dp[j] is true
            if curr.isEndOfWord and dp[j]:
                dp[i] = True
                break

    return dp[n]

# Driver program to test above functions
if __name__ == '__main__':
    dictionary = [
        "mobile", "samsung", "sam", "sung", "ma",
        "mango", "icecream", "and", "go", "i",
        "like", "ice", "cream"
    ]

    root = TNode()

    # Construct trie
    for word in dictionary:
        insert(root, word)

    print("Yes" if wordBreak("ilikesamsung", root) else "No")
    print("Yes" if wordBreak("iiiiiiii", root) else "No")
    print("Yes" if wordBreak("", root) else "No")
    print("Yes" if wordBreak("ilikelikeimangoiii", root) else "No")
    print("Yes" if wordBreak("samsungandmango", root) else "No")
    print("Yes" if wordBreak("samsungandmangok", root) else "No")
C#
using System;
using System.Collections.Generic;

class TNode {
    public TNode[] child;
    public bool isEndOfWord;

    public TNode() {
        child = new TNode[26];
        isEndOfWord = false;
    }
}

public class WordBreak {
    static readonly int ALPHABET_SIZE = 26;

    // If not present, inserts key into trie
    // If the key is prefix of trie node, just marks leaf node
    static void Insert(TNode root, string key) {
        TNode pCrawl = root;

        for (int i = 0; i < key.Length; i++) {
            int index = key[i] - 'a';
            if (pCrawl.child[index] == null)
                pCrawl.child[index] = new TNode();

            pCrawl = pCrawl.child[index];
        }

        // mark last node as leaf
        pCrawl.isEndOfWord = true;
    }

    // Returns true if string can be segmented into
    // space separated words, otherwise returns false
    static bool WordBreakFunc(string str, TNode root) {
        int n = str.Length;
        if (n == 0)
            return true;

        bool[] dp = new bool[n + 1];

        // dp[i] is true if str[0..i-1] can be segmented
        dp[0] = true;

        for (int i = 1; i <= n; i++) {
            TNode curr = root;

            for (int j = i - 1; j >= 0; j--) {
                int index = str[j] - 'a';

                if (curr.child[index] == null)
                    break;

                curr = curr.child[index];

                // If str[j..i-1] is a word and dp[j] is true
                if (curr.isEndOfWord && dp[j]) {
                    dp[i] = true;
                    break;
                }
            }
        }

        return dp[n];
    }

    public static void Main(string[] args) {
        string[] dictionary = {
            "mobile", "samsung", "sam", "sung", "ma",
            "mango", "icecream", "and", "go", "i",
            "like", "ice", "cream"
        };

        TNode root = new TNode();

        // Construct trie
        foreach (string word in dictionary)
            Insert(root, word);

        Console.WriteLine(WordBreakFunc("ilikesamsung", root) ? "Yes" : "No");
        Console.WriteLine(WordBreakFunc("iiiiiiii", root) ? "Yes" : "No");
        Console.WriteLine(WordBreakFunc("", root) ? "Yes" : "No");
        Console.WriteLine(WordBreakFunc("ilikelikeimangoiii", root) ? "Yes" : "No");
        Console.WriteLine(WordBreakFunc("samsungandmango", root) ? "Yes" : "No");
        Console.WriteLine(WordBreakFunc("samsungandmangok", root) ? "Yes" : "No");
    }
}
JavaScript
// Node class for Trie
class TNode {
    constructor() {
        this.child = new Array(26).fill(null);
        this.isEndOfWord = false;
    }
}

// If not present, inserts key into trie
// If the key is prefix of trie node, just marks leaf node
function insert(root, key) {
    let pCrawl = root;

    for (let char of key) {
        const index = char.charCodeAt(0) - 'a'.charCodeAt(0);
        if (!pCrawl.child[index]) {
            pCrawl.child[index] = new TNode();
        }
        pCrawl = pCrawl.child[index];
    }

    // mark last node as leaf
    pCrawl.isEndOfWord = true;
}

// Returns true if string can be segmented into space separated words, otherwise returns false
function wordBreak(s, root) {
    const n = s.length;
    if (n === 0) {
        return true;
    }

    const dp = new Array(n + 1).fill(false);
    dp[0] = true;  // dp[i] is true if s[0..i-1] can be segmented

    for (let i = 1; i <= n; i++) {
        let curr = root;
        for (let j = i - 1; j >= 0; j--) {
            const index = s.charCodeAt(j) - 'a'.charCodeAt(0);
            if (!curr.child[index]) {
                break;
            }
            curr = curr.child[index];
            // If s[j..i-1] is a word and dp[j] is true
            if (curr.isEndOfWord && dp[j]) {
                dp[i] = true;
                break;
            }
        }
    }

    return dp[n];
}

// Driver program to test above functions
const dictionary = [
    'mobile', 'samsung', 'sam', 'sung', 'ma',
    'mango', 'icecream', 'and', 'go', 'i',
    'like', 'ice', 'cream'
];

const root = new TNode();

// Construct trie
for (const word of dictionary) {
    insert(root, word);
}

console.log(wordBreak('ilikesamsung', root) ? 'Yes' : 'No');
console.log(wordBreak('iiiiiiii', root) ? 'Yes' : 'No');
console.log(wordBreak('', root) ? 'Yes' : 'No');
console.log(wordBreak('ilikelikeimangoiii', root) ? 'Yes' : 'No');
console.log(wordBreak('samsungandmango', root) ? 'Yes' : 'No');
console.log(wordBreak('samsungandmangok', root) ? 'Yes' : 'No');

Output
Yes
Yes
Yes
Yes
Yes
No

Time Complexity: O(n*m) where n is the length of the string and m is the length of the longest word in the dictionary. 
Auxiliary Space: O(n*m)


Similar Reads