Info

#include <iostream>
#include <optional>
#include <print>
#include <unordered_map>
#include <vector>
 
class Trie {
private:
    class TrieNode {
    public:
        bool isWord = false;
        std::unordered_map<char, TrieNode*> children = {};
    };
 
    TrieNode* root = new TrieNode();
 
public:
    void addWord(std::string_view const& str) {
        if (str.empty()) {
            return;
        }
 
        auto curNode = root;
        for (auto const& c: str) {
            auto& nextNode = curNode->children[c];
            if (!nextNode) {
                nextNode = new TrieNode();
            }
            curNode = nextNode;
        }
        curNode->isWord = true;
    }
 
    template<typename Callback>
    bool matchPrefixLengths(std::string_view const& str, Callback&& callback) const {
        auto curNode = root;
        int prefixLength = 0;
 
        for (auto const& c: str) {
            ++prefixLength;
            curNode = curNode->children[c];
            if (!curNode) {
                return false;
            }
            if (curNode->isWord) {
                if (callback(prefixLength)) {
                    return true;
                }
            }
        }
 
        return false;
    }
};
 
bool wordBreak(std::string_view const& str, std::vector<std::string> const& wordDict) {
    auto trie = Trie();
    for (auto const& word: wordDict) {
        trie.addWord(word);
    }
 
    auto isWordBreakPossibleDp = std::unordered_map<std::string_view, std::optional<bool>> {};
 
    auto isWordBreakPossible = [&](this auto&& isWordBreakPossible, std::string_view const& str) -> bool {
        std::println("isWordBreakPossible called | str: {}", str);
 
        auto& dpVal = isWordBreakPossibleDp[str];
        if (dpVal) {
            return *dpVal;
        }
        
        if (str.empty()) {
            dpVal = true;
            return *dpVal;
        }
 
        dpVal = trie.matchPrefixLengths(str, [&](auto const& matchedPrefixLength) -> bool {
            auto remainingStr = std::string_view { str.begin() + matchedPrefixLength, str.end() };
            return isWordBreakPossible(remainingStr);
        });
        
        return *dpVal;
    };
 
    return isWordBreakPossible(str);
}
 
int main() {
    using namespace std::literals;
    auto words = std::vector<std::string> {"cat", "sand", "sands", "cats", "an", "and", "do", "dog"};
    std::println("answer: {}", wordBreak("catsanddogs"s, words));
}