From a2970af4c44710f5e92b176e526b210d7054c750 Mon Sep 17 00:00:00 2001 From: Ashish Bhanu Daulatabad Date: Fri, 12 Mar 2021 00:33:15 +0530 Subject: [PATCH] feat: add abbreviation.cpp: topic: Dynamic Programming (#1461) * Feat: Abbreivation problem (abbreviation.cpp), topic: Dynamic Programming. * Spelling correction * Spelling Mistakes * Refactoring code * updating DIRECTORY.md * clang-format and clang-tidy fixes for 232d4f5e * clang-format and clang-tidy fixes for 232d4f5e * Documentation and renaming variable * adding a comment * adding a comment * Re-documentation * Re-documentation * Re-documentation * Update dynamic_programming/abbreviation.cpp Co-authored-by: David Leal * Update dynamic_programming/abbreviation.cpp Co-authored-by: David Leal * Update dynamic_programming/abbreviation.cpp Co-authored-by: David Leal * Varaible type-checks * Update dynamic_programming/abbreviation.cpp Co-authored-by: David Leal * clang-format and clang-tidy fixes for 8f5a50be * Varaible type-checks * Markdown correction * Comment Modification Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: David Leal --- DIRECTORY.md | 2 + dynamic_programming/abbreviation.cpp | 194 +++++++++++++++++++++++++++ 2 files changed, 196 insertions(+) create mode 100644 dynamic_programming/abbreviation.cpp diff --git a/DIRECTORY.md b/DIRECTORY.md index e8be6a9c5..239b9d539 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -42,6 +42,7 @@ * [Queue Using Linked List](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/queue_using_linked_list.cpp) * [Queue Using Linkedlist](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/queue_using_linkedlist.cpp) * [Queue Using Two Stacks](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/queue_using_two_stacks.cpp) + * [Rb Tree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/rb_tree.cpp) * [Skip List](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/skip_list.cpp) * [Stack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/stack.h) * [Stack Using Array](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/stack_using_array.cpp) @@ -56,6 +57,7 @@ ## Dynamic Programming * [0 1 Knapsack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/0_1_knapsack.cpp) + * [Abbreviation](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/abbreviation.cpp) * [Armstrong Number](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/armstrong_number.cpp) * [Bellman Ford](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/bellman_ford.cpp) * [Catalan Numbers](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/catalan_numbers.cpp) diff --git a/dynamic_programming/abbreviation.cpp b/dynamic_programming/abbreviation.cpp new file mode 100644 index 000000000..9ee4dc274 --- /dev/null +++ b/dynamic_programming/abbreviation.cpp @@ -0,0 +1,194 @@ +/** + * @file + * @brief Implementation of + * [Abbrievation](https://www.hackerrank.com/challenges/abbr/problem) + * + * @details + * Given two strings, `a` and `b`, determine if it's possible to make `a` equal + * to `b` You can perform the following operations on the string `a`: + * 1. Capitalize zero or more of `a`'s lowercase letters. + * 2. Delete all of the remaining lowercase letters in `a`. + * + * ### Algorithm + * The idea is in the problem statement itself: iterate through characters of + * string `a` and `b` (for character indexes `i` and `j` respectively): + * 1. If `a[i]` and `b[j]` are equal, then move to next position + * 2. If `a[i]` is lowercase of `b[j]`, then explore two possibilities: + * a. Capitalize `a[i]` or + * b. Skip `a[i]` + * 3. If the `a[i]` is not uppercase, just discard that character, else return + * `false` + * + * Time Complexity: (O(|a|*|b|)) where `|a|` => length of string `a` + * @author [Ashish Daulatabad](https://github.com/AshishYUO) + */ + +#include /// for `assert` +#include /// for IO operations +#include /// for `std::string` library +#include /// for `std::vector` STL library +/** + * @namespace dynamic_programming + * @brief Dynamic Programming Algorithms + */ +namespace dynamic_programming { +/** + * @namespace abbreviation + * @brief Functions for + * [Abbreviation](https://www.hackerrank.com/challenges/abbr/problem) + * implementation + */ +namespace abbreviation { +/** + * @brief + * Recursive Dynamic Programming function + * @details + * Returns whether `s` can be converted to `t` with following rules: + * a. Capitalize zero or more of a's lowercase letters from string `s` + * b. remove all other lowercase letters from string `s` + * @param memo To store the result + * @param visited boolean to check if the result is already computed + * @param str given string, which might not be abbreivated + * @param result resultant abbreivated string + * @param str_idx index for string `str`, helpful for transitions + * @param result_idx index for string `result`, helpful for transitions + * @returns `false` if string `str` cannot be converted to `result` + * @returns `true` if string `str` can be converted to `result` + */ +bool abbreviation_recursion(std::vector> *memo, + std::vector> *visited, + const std::string &str, const std::string &result, + uint32_t str_idx = 0, uint32_t result_idx = 0) { + bool ans = memo->at(str_idx).at(result_idx); + if (str_idx == str.size() && result_idx == result.size()) { + return true; + } else if (str_idx == str.size() && result_idx != result.size()) { + // result `t` is not converted, return false + return false; + } else if (!visited->at(str_idx).at(result_idx)) { + /** + * `(str[i] == result[j])`: if str char at position i is equal to + * `result` char at position j, then s character is a capitalized one, + * move on to next character `str[i] - 32 == result[j]`: + * if `str[i]` character is lowercase of `result[j]` then explore two + * possibilites: + * 1. convert it to capitalized letter and move both to next pointer + * `(i + 1, j + 1)` + * 2. Discard the character `(str[i])` and move to next char `(i + 1, + * j)` + */ + if (str[str_idx] == result[result_idx]) { + ans = abbreviation_recursion(memo, visited, str, result, + str_idx + 1, result_idx + 1); + } else if (str[str_idx] - 32 == result[result_idx]) { + ans = abbreviation_recursion(memo, visited, str, result, + str_idx + 1, result_idx + 1) || + abbreviation_recursion(memo, visited, str, result, + str_idx + 1, result_idx); + } else { + // if `str[i]` is uppercase, then cannot be converted, return + // `false` + // else `str[i]` is lowercase, only option is to discard this + // character + if (str[str_idx] >= 'A' && str[str_idx] <= 'Z') { + ans = false; + } else { + ans = abbreviation_recursion(memo, visited, str, result, + str_idx + 1, result_idx); + } + } + } + (*memo)[str_idx][result_idx] = ans; + (*visited)[str_idx][result_idx] = true; + return (*memo)[str_idx][result_idx]; +} +/** + * @brief + * Iterative Dynamic Programming function + * @details + * Returns whether `s` can be converted to `t` with following rules: + * a. Capitalize zero or more of s's lowercase letters from string `s` + * b. remove all other lowercase letters from string `s` + * Note: The transition states for iterative is similar to recursive as well + * @param str given string, which might not be abbreivated + * @param result resultant abbreivated string + * @returns `false` if string `str` cannot be converted to `result` + * @returns `true` if string `str` can be converted to `result` + */ +bool abbreviation(const std::string &str, const std::string &result) { + std::vector> memo( + str.size() + 1, std::vector(result.size() + 1, false)); + + for (uint32_t i = 0; i <= str.size(); ++i) { + memo[i][0] = true; + } + for (uint32_t i = 1; i <= result.size(); ++i) { + memo[0][i] = false; + } + for (uint32_t i = 1; i <= str.size(); ++i) { + for (uint32_t j = 1; j <= result.size(); ++j) { + if (str[i - 1] == result[j - 1]) { + memo[i][j] = memo[i - 1][j - 1]; + } else if (str[i - 1] - 32 == result[j - 1]) { + memo[i][j] = (memo[i - 1][j - 1] || memo[i - 1][j]); + } else { + if (str[i - 1] >= 'A' && str[i - 1] <= 'Z') { + memo[i][j] = false; + } else { + memo[i][j] = memo[i - 1][j]; + } + } + } + } + return memo.back().back(); +} +} // namespace abbreviation +} // namespace dynamic_programming + +/** + * @brief Self test-implementations + * @returns void + */ +static void test() { + std::string s = "daBcd", t = "ABC"; + std::vector> memo(s.size() + 1, + std::vector(t.size() + 1, false)), + visited(s.size() + 1, std::vector(t.size() + 1, false)); + + assert(dynamic_programming::abbreviation::abbreviation_recursion( + &memo, &visited, s, t) == true); + assert(dynamic_programming::abbreviation::abbreviation(s, t) == true); + s = "XXVVnDEFYgYeMXzWINQYHAQKKOZEYgSRCzLZAmUYGUGILjMDET"; + t = "XXVVDEFYYMXWINQYHAQKKOZEYSRCLZAUYGUGILMDETQVWU"; + memo = std::vector>( + s.size() + 1, std::vector(t.size() + 1, false)); + + visited = std::vector>( + s.size() + 1, std::vector(t.size() + 1, false)); + + assert(dynamic_programming::abbreviation::abbreviation_recursion( + &memo, &visited, s, t) == false); + assert(dynamic_programming::abbreviation::abbreviation(s, t) == false); + + s = "DRFNLZZVHLPZWIupjwdmqafmgkg"; + t = "DRFNLZZVHLPZWI"; + + memo = std::vector>( + s.size() + 1, std::vector(t.size() + 1, false)); + + visited = std::vector>( + s.size() + 1, std::vector(t.size() + 1, false)); + + assert(dynamic_programming::abbreviation::abbreviation_recursion( + &memo, &visited, s, t) == true); + assert(dynamic_programming::abbreviation::abbreviation(s, t) == true); +} + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + test(); // run self-test implementations + return 0; +}