package com.freetymekiyan.algorithms.level.hard; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * Given a string s and a dictionary of words dict, add spaces in s to * construct a sentence where each word is a valid dictionary word. * <p> * Return all such possible sentences. * <p> * For example, given * s = "catsanddog", * dict = ["cat", "cats", "and", "sand", "dog"]. * <p> * A solution is ["cats and dog", "cat sand dog"]. * <p> * Tags: DP, Backtracking */ class WordBreak2 { /** * Memory function * Store how a word can be decomposed */ Map<String, List<String>> res = new HashMap<String, List<String>>(); /** * DP, Backtracking * Store successful decomposition in a map * Get prefix * If not in dictionary, just ignore * If in dictionary, check current position * If reaches the end, add prefix to a solution * If within length do the following: * Check whether the rest of the string is already decomposed * If not, backtracking the rest of the string * If yes, get the result from memory function * If there is an result, add each word to current solution with front in */ public List<String> wordBreak(String s, Set<String> dict) { List<String> words = new ArrayList<String>(); int len = s.length(); for (int i = 1; i <= len; i++) { String pref = s.substring(0, i); if (dict.contains(pref)) { if (i == len) { words.add(pref); // reach the end } else { String remain = s.substring(i, len); // remaining string List<String> remainDecomp = res.containsKey(remain) ? res.get(remain) : wordBreak(remain, dict); // avoid backtracking if a decomposition is already there if (remainDecomp != null) { for (String w : remainDecomp) { words.add(pref + " " + w); } res.put(remain, remainDecomp); // add to cache } } } } return words; } /** * Backtracking * Get prefix first * If prefix is in dictionary, check current length * If reaches the end, add prefix to result * If not, go ahead and decompose the remain string * Get the result list, and concat prefix with those results * addRecursive the concatenated string to result and return */ public List<String> wordBreakB(String s, Set<String> dict) { List<String> words = new ArrayList<String>(); int len = s.length(); for (int i = 1; i <= len; i++) { String pref = s.substring(0, i); if (dict.contains(pref)) { if (i == len) { words.add(pref); } else { String remain = s.substring(i, len); List<String> remainDecomp = wordBreakB(remain, dict); if (remainDecomp != null) { // has decompositions for (String item : remainDecomp) { words.add(pref + " " + item); } } } } } return words; } }