From 34a6faf401ffaff920bf8a9c89c665eb63095697 Mon Sep 17 00:00:00 2001 From: Akshay Anand Date: Sat, 17 Oct 2020 01:24:42 +0530 Subject: [PATCH] adding word break DP approach (#1278) * adding word break DP approach * fixing formatting * fixing linting issues * adding documentation and other enhancements * Update dynamic_programming/word_break.cpp Co-authored-by: David Leal * Update dynamic_programming/word_break.cpp Co-authored-by: David Leal * Update dynamic_programming/word_break.cpp Co-authored-by: David Leal * Update dynamic_programming/word_break.cpp Co-authored-by: David Leal * Update dynamic_programming/word_break.cpp Co-authored-by: David Leal * updating DIRECTORY.md * clang-format and clang-tidy fixes for 061c21ae * docs: fixed documentation * Update dynamic_programming/word_break.cpp Co-authored-by: David Leal * Update dynamic_programming/word_break.cpp Co-authored-by: David Leal * Update dynamic_programming/word_break.cpp Co-authored-by: David Leal * Update dynamic_programming/word_break.cpp Co-authored-by: David Leal * Update dynamic_programming/word_break.cpp Co-authored-by: David Leal * clang-format and clang-tidy fixes for d8ab6b02 * Update dynamic_programming/word_break.cpp Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * clang-format and clang-tidy fixes for 05d7ca14 Co-authored-by: David Leal Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> --- DIRECTORY.md | 1 + dynamic_programming/word_break.cpp | 186 +++++++++++++++++++++++++++++ 2 files changed, 187 insertions(+) create mode 100644 dynamic_programming/word_break.cpp diff --git a/DIRECTORY.md b/DIRECTORY.md index 471980365..afe01d21a 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -69,6 +69,7 @@ * [Searching Of Element In Dynamic Array](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/searching_of_element_in_dynamic_array.cpp) * [Shortest Common Supersequence](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/shortest_common_supersequence.cpp) * [Tree Height](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/tree_height.cpp) + * [Word Break](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/word_break.cpp) ## Geometry * [Jarvis Algorithm](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/geometry/jarvis_algorithm.cpp) diff --git a/dynamic_programming/word_break.cpp b/dynamic_programming/word_break.cpp new file mode 100644 index 000000000..5291f665f --- /dev/null +++ b/dynamic_programming/word_break.cpp @@ -0,0 +1,186 @@ +/** + * @file + * @brief [Word Break Problem](https://leetcode.com/problems/word-break/) + * @details + * Given a non-empty string s and a dictionary wordDict containing a list of + * non-empty words, determine if s can be segmented into a space-separated + * sequence of one or more dictionary words. + * + * Note: + * The same word in the dictionary may be reused multiple times in the + * segmentation. You may assume the dictionary does not contain duplicate words. + * + * 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. + * + * Example 3: + * Input: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"] + * Output: false + * + * @author [Akshay Anand] (https://github.com/axayjha) + */ + +#include +#include +#include +#include +#include +#include + +/** + * @namespace dynamic_programming + * @brief Dynamic programming algorithms + */ +namespace dynamic_programming { + +/** + * @namespace word_break + * @brief Functions for [Word Break](https://leetcode.com/problems/word-break/) + * problem + */ +namespace word_break { + +/** + * @brief Function that checks if the string passed in param is present in + * the the unordered_set passed + * + * @param str the string to be searched + * @param strSet unordered set of string, that is to be looked into + * @returns `true` if str is present in strSet + * @returns `false` if str is not present in strSet + */ +bool exists(const std::string &str, + const std::unordered_set &strSet) { + return strSet.find(str) != strSet.end(); +} + +/** + * @brief Function that checks if the string passed in param can be + * segmented from position 'pos', and then correctly go on to segment the + * rest of the string correctly as well to reach a solution + * + * @param s the complete string to be segmented + * @param strSet unordered set of string, that is to be used as the + * reference dictionary + * @param pos the index value at which we will segment string and test + * further if it is correctly segmented at pos + * @param dp the vector to memoize solution for each position + * @returns `true` if a valid solution/segmentation is possible by segmenting at + * index pos + * @returns `false` otherwise + */ +bool check(const std::string &s, const std::unordered_set &strSet, + int pos, std::vector *dp) { + if (pos == s.length()) { + // if we have reached till the end of the string, means we have + // segmented throughout correctly hence we have a solution, thus + // returning true + return true; + } + + if (dp->at(pos) != INT_MAX) { + // if dp[pos] is not INT_MAX, means we must have saved a solution + // for the position pos; then return if the solution at pos is true + // or not + return dp->at(pos) == 1; + } + + std::string wordTillNow = + ""; // string to save the prefixes of word till different positons + + for (int i = pos; i < s.length(); i++) { + // Loop starting from pos to end, to check valid set of + // segmentations if any + wordTillNow += + std::string(1, s[i]); // storing the prefix till the position i + + // if the prefix till current position is present in the dictionary + // and the remaining substring can also be segmented legally, then + // set solution at position pos in the memo, and return true + if (exists(wordTillNow, strSet) and check(s, strSet, i + 1, dp)) { + dp->at(pos) = 1; + return true; + } + } + // if function has still not returned, then there must be no legal + // segmentation possible after segmenting at pos + dp->at(pos) = 0; // so set solution at pos as false + return false; // and return no solution at position pos +} + +/** + * @brief Function that checks if the string passed in param can be + * segmented into the strings present in the vector. + * In others words, it checks if any permutation of strings in + * the vector can be concatenated to form the final string. + * + * @param s the complete string to be segmented + * @param wordDict a vector of words to be used as dictionary to look into + * @returns `true` if s can be formed by a combination of strings present in + * wordDict + * @return `false` otherwise + */ +bool wordBreak(const std::string &s, const std::vector &wordDict) { + // unordered set to store words in the dictionary for constant time + // search + std::unordered_set strSet; + for (const auto &s : wordDict) { + strSet.insert(s); + } + // a vector to be used for memoization, whose value at index i will + // tell if the string s can be segmented (correctly) at position i. + // initializing it with INT_MAX (which will denote no solution) + std::vector dp(s.length(), INT_MAX); + + // calling check method with position = 0, to check from left + // from where can be start segmenting the complete string in correct + // manner + return check(s, strSet, 0, &dp); +} + +} // namespace word_break +} // namespace dynamic_programming + +/** + * @brief Test implementations + * @returns void + */ +static void test() { + // the complete string + const std::string s = "applepenapple"; + // the dictionary to be used + const std::vector wordDict = {"apple", "pen"}; + + assert(dynamic_programming::word_break::wordBreak(s, wordDict)); + + // should return true, as applepenapple can be segmented as apple + pen + + // apple + std::cout << dynamic_programming::word_break::wordBreak(s, wordDict) + << std::endl; + std::cout << "Test implementation passed!\n"; +} +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + test(); // call the test function :) + + // the complete string + const std::string s = "applepenapple"; + // the dictionary to be used + const std::vector wordDict = {"apple", "pen"}; + + // should return true, as applepenapple can be segmented as apple + pen + + // apple + std::cout << dynamic_programming::word_break::wordBreak(s, wordDict) + << std::endl; +}