chore: import upstream snapshot with attribution
Awesome CI Workflow / Code Formatter (push) Waiting to run
Awesome CI Workflow / Compile checks (macOS-latest) (push) Blocked by required conditions
Awesome CI Workflow / Compile checks (ubuntu-latest) (push) Blocked by required conditions
Awesome CI Workflow / Compile checks (windows-latest) (push) Blocked by required conditions

This commit is contained in:
wehub-resource-sync
2026-07-13 12:37:28 +08:00
commit 29cfe479ab
432 changed files with 68491 additions and 0 deletions
+130
View File
@@ -0,0 +1,130 @@
/**
* @file
* @brief Implementation of [0-1 Knapsack Problem]
* (https://en.wikipedia.org/wiki/Knapsack_problem)
*
* @details
* Given weights and values of n items, put these items in a knapsack of
* capacity `W` to get the maximum total value in the knapsack. In other words,
* given two integer arrays `val[0..n-1]` and `wt[0..n-1]` which represent
* values and weights associated with n items respectively. Also given an
* integer W which represents knapsack capacity, find out the maximum value
* subset of `val[]` such that sum of the weights of this subset is smaller than
* or equal to W. You cannot break an item, either pick the complete item or
* dont pick it (0-1 property)
*
* ### Algorithm
* The idea is to consider all subsets of items and calculate the total weight
* and value of all subsets. Consider the only subsets whose total weight is
* smaller than `W`. From all such subsets, pick the maximum value subset.
*
* @author [Anmol](https://github.com/Anmol3299)
* @author [Pardeep](https://github.com/Pardeep009)
*/
#include <array>
#include <cassert>
#include <iostream>
#include <vector>
/**
* @namespace dynamic_programming
* @brief Dynamic Programming algorithms
*/
namespace dynamic_programming {
/**
* @namespace Knapsack
* @brief Implementation of 0-1 Knapsack problem
*/
namespace knapsack {
/**
* @brief Picking up all those items whose combined weight is below
* the given capacity and calculating the value of those picked items. Trying all
* possible combinations will yield the maximum knapsack value.
* @tparam n size of the weight and value array
* @param capacity capacity of the carrying bag
* @param weight array representing the weight of items
* @param value array representing the value of items
* @return maximum value obtainable with a given capacity.
*/
template <size_t n>
int maxKnapsackValue(const int capacity, const std::array<int, n> &weight,
const std::array<int, n> &value) {
std::vector<std::vector<int> > maxValue(n + 1,
std::vector<int>(capacity + 1, 0));
// outer loop will select no of items allowed
// inner loop will select the capacity of the knapsack bag
int items = sizeof(weight) / sizeof(weight[0]);
for (size_t i = 0; i < items + 1; ++i) {
for (size_t j = 0; j < capacity + 1; ++j) {
if (i == 0 || j == 0) {
// if no of items is zero or capacity is zero, then maxValue
// will be zero
maxValue[i][j] = 0;
} else if (weight[i - 1] <= j) {
// if the ith item's weight(in the actual array it will be at i-1)
// is less than or equal to the allowed weight i.e. j then we
// can pick that item for our knapsack. maxValue will be the
// obtained either by picking the current item or by not picking
// current item
// picking the current item
int profit1 = value[i - 1] + maxValue[i - 1][j - weight[i - 1]];
// not picking the current item
int profit2 = maxValue[i - 1][j];
maxValue[i][j] = std::max(profit1, profit2);
} else {
// as the weight of the current item is greater than the allowed weight, so
// maxProfit will be profit obtained by excluding the current item.
maxValue[i][j] = maxValue[i - 1][j];
}
}
}
// returning maximum value
return maxValue[items][capacity];
}
} // namespace knapsack
} // namespace dynamic_programming
/**
* @brief Function to test the above algorithm
* @returns void
*/
static void test() {
// Test 1
const int n1 = 3; // number of items
std::array<int, n1> weight1 = {10, 20, 30}; // weight of each item
std::array<int, n1> value1 = {60, 100, 120}; // value of each item
const int capacity1 = 50; // capacity of carrying bag
const int max_value1 = dynamic_programming::knapsack::maxKnapsackValue(
capacity1, weight1, value1);
const int expected_max_value1 = 220;
assert(max_value1 == expected_max_value1);
std::cout << "Maximum Knapsack value with " << n1 << " items is "
<< max_value1 << std::endl;
// Test 2
const int n2 = 4; // number of items
std::array<int, n2> weight2 = {24, 10, 10, 7}; // weight of each item
std::array<int, n2> value2 = {24, 18, 18, 10}; // value of each item
const int capacity2 = 25; // capacity of carrying bag
const int max_value2 = dynamic_programming::knapsack::maxKnapsackValue(
capacity2, weight2, value2);
const int expected_max_value2 = 36;
assert(max_value2 == expected_max_value2);
std::cout << "Maximum Knapsack value with " << n2 << " items is "
<< max_value2 << std::endl;
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
// Testing
test();
return 0;
}
+18
View File
@@ -0,0 +1,18 @@
# If necessary, use the RELATIVE flag, otherwise each source file may be listed
# with full pathname. RELATIVE may makes it easier to extract an executable name
# automatically.
file( GLOB APP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp )
# file( GLOB APP_SOURCES ${CMAKE_SOURCE_DIR}/*.c )
# AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} APP_SOURCES)
foreach( testsourcefile ${APP_SOURCES} )
# I used a simple string replace, to cut off .cpp.
string( REPLACE ".cpp" "" testname ${testsourcefile} )
add_executable( ${testname} ${testsourcefile} )
set_target_properties(${testname} PROPERTIES LINKER_LANGUAGE CXX)
if(OpenMP_CXX_FOUND)
target_link_libraries(${testname} OpenMP::OpenMP_CXX)
endif()
install(TARGETS ${testname} DESTINATION "bin/dynamic_programming")
endforeach( testsourcefile ${APP_SOURCES} )
+195
View File
@@ -0,0 +1,195 @@
/**
* @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 <cassert> /// for `assert`
#include <cstdint> /// for `std::uint32_t`
#include <iostream> /// for IO operations
#include <string> /// for `std::string` library
#include <vector> /// 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<std::vector<bool>> *memo,
std::vector<std::vector<bool>> *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<std::vector<bool>> memo(
str.size() + 1, std::vector<bool>(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<std::vector<bool>> memo(s.size() + 1,
std::vector<bool>(t.size() + 1, false)),
visited(s.size() + 1, std::vector<bool>(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<std::vector<bool>>(
s.size() + 1, std::vector<bool>(t.size() + 1, false));
visited = std::vector<std::vector<bool>>(
s.size() + 1, std::vector<bool>(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<std::vector<bool>>(
s.size() + 1, std::vector<bool>(t.size() + 1, false));
visited = std::vector<std::vector<bool>>(
s.size() + 1, std::vector<bool>(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;
}
@@ -0,0 +1,93 @@
/**
* @file
* @brief Checks whether a number is an [Armstrong
* Number](https://en.wikipedia.org/wiki/Narcissistic_number) or not.
*
* @details
* An Armstrong number is a number that is the sum of its own digits each raised
* to the power of the number of digits. For example: 153 is an Armstrong number
* since 153 = 1^3 + 5^3 + 3^3.
*
* A few examples of valid armstrong numbers:
* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 153, 370, 371, 407, 1634, 8208, 9474, 54748,
* 92727, 93084.
*
* Armstrong numbers are also known as Narcissistic Numbers, as stated in
* Wikipedia.
*
* @author [Shivam Singhal](https://github.com/shivhek25)
* @author [David Leal](https://github.com/Panquesito7)
*/
#include <cassert> /// for assert
#include <cmath> /// for std::pow
#include <iostream> /// for IO operations
/**
* @namespace
* @brief Dynamic Programming algorithms
*/
namespace dynamic_programming {
/**
* @brief Checks if the given number is armstrong or not.
* @param number the number to check
* @returns false if the given number is NOT armstrong
* @returns true if the given number IS armstrong
*/
template <typename T>
bool is_armstrong(const T &number) {
int count = 0, temp = number, result = 0, rem = 0;
// Count the number of digits of the given number.
// For example: 153 would be 3 digits.
while (temp != 0) {
temp /= 10;
count++;
}
// Calculation for checking of armstrongs number i.e.
// in an n-digit number sum of the digits is raised to a power of `n` is
// equal to the original number.
temp = number;
while (temp != 0) {
rem = temp % 10;
result += static_cast<T>(std::pow(rem, count));
temp /= 10;
}
if (result == number) {
return true;
} else {
return false;
}
}
} // namespace dynamic_programming
/**
* @brief Self-test implementations
* @returns void
*/
static void tests() {
assert(dynamic_programming::is_armstrong(153) == true);
assert(dynamic_programming::is_armstrong(1) == true);
assert(dynamic_programming::is_armstrong(0) == true);
assert(dynamic_programming::is_armstrong(370) == true);
assert(dynamic_programming::is_armstrong(1634) == true);
assert(dynamic_programming::is_armstrong(580) == false);
assert(dynamic_programming::is_armstrong(15) == false);
assert(dynamic_programming::is_armstrong(1024) == false);
assert(dynamic_programming::is_armstrong(989) == false);
assert(dynamic_programming::is_armstrong(103) == false);
std::cout << "All tests have successfully passed!\n";
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
tests(); // run self-test implementations
return 0;
}
+118
View File
@@ -0,0 +1,118 @@
#include <climits>
#include <iostream>
#include <vector>
using namespace std;
// Wrapper class for storing an edge
class Edge {
public:
int src, dst, weight;
};
// Wrapper class for storing a graph
class Graph {
public:
int vertexNum, edgeNum;
std::vector<Edge> edges;
// Constructs a graph with V vertices and E edges
Graph(int V, int E) {
this->vertexNum = V;
this->edgeNum = E;
this->edges.reserve(E);
}
// Adds the given edge to the graph
void addEdge(int src, int dst, int weight) {
static int edgeInd = 0;
if (edgeInd < this->edgeNum) {
Edge newEdge;
newEdge.src = src;
newEdge.dst = dst;
newEdge.weight = weight;
this->edges[edgeInd++] = newEdge;
}
}
};
// Utility function to print distances
void print(const std::vector<int>& dist, int V) {
cout << "\nVertex Distance" << endl;
for (int i = 0; i < V; i++) {
if (dist[i] != INT_MAX)
cout << i << "\t" << dist[i] << endl;
else
cout << i << "\tINF" << endl;
}
}
// The main function that finds the shortest path from given source
// to all other vertices using Bellman-Ford.It also detects negative
// weight cycle
void BellmanFord(Graph graph, int src) {
int V = graph.vertexNum;
int E = graph.edgeNum;
std::vector<int> dist;
dist.reserve(E);
// Initialize distances array as INF for all except source
// Intialize source as zero
for (int i = 0; i < V; i++) dist[i] = INT_MAX;
dist[src] = 0;
// Calculate shortest path distance from source to all edges
// A path can contain maximum (|V|-1) edges
for (int i = 0; i <= V - 1; i++)
for (int j = 0; j < E; j++) {
int u = graph.edges[j].src;
int v = graph.edges[j].dst;
int w = graph.edges[j].weight;
if (dist[u] != INT_MAX && dist[u] + w < dist[v])
dist[v] = dist[u] + w;
}
// Iterate inner loop once more to check for negative cycle
for (int j = 0; j < E; j++) {
int u = graph.edges[j].src;
int v = graph.edges[j].dst;
int w = graph.edges[j].weight;
if (dist[u] != INT_MAX && dist[u] + w < dist[v]) {
cout << "Graph contains negative weight cycle. Hence, shortest "
"distance not guaranteed."
<< endl;
return;
}
}
print(dist, V);
return;
}
// Driver Function
int main() {
int V, E, gsrc;
int src, dst, weight;
cout << "Enter number of vertices: ";
cin >> V;
cout << "Enter number of edges: ";
cin >> E;
Graph G(V, E);
for (int i = 0; i < E; i++) {
cout << "\nEdge " << i + 1 << "\nEnter source: ";
cin >> src;
cout << "Enter destination: ";
cin >> dst;
cout << "Enter weight: ";
cin >> weight;
G.addEdge(src, dst, weight);
}
cout << "\nEnter source: ";
cin >> gsrc;
BellmanFord(G, gsrc);
return 0;
}
+82
View File
@@ -0,0 +1,82 @@
/**
* @file
* @brief Provides utilities to compute Catalan numbers using dynamic
programming.
* A Catalan numbers satisfy these recurrence relations:
* C(0) = C(1) = 1; C(n) = sum(C(i).C(n-i-1)), for i = 0 to n-1
* Read more about Catalan numbers here:
https://en.wikipedia.org/wiki/Catalan_number
https://oeis.org/A000108/
*/
#include <cassert> /// for assert
#include <cstdint> /// for std::uint64_t
#include <cstdlib> /// for std::size_t
#include <functional> /// for std::plus & std::multiplies
#include <numeric> /// for std::transform_reduce
#include <vector> /// for std::vector
/**
* @brief computes and caches Catalan numbers
*/
class catalan_numbers {
using value_type = std::uint64_t;
std::vector<value_type> known{1, 1};
value_type compute_next() {
return std::transform_reduce(known.begin(), known.end(), known.rbegin(),
static_cast<value_type>(0), std::plus<>(),
std::multiplies<>());
}
void add() { known.push_back(this->compute_next()); }
public:
/**
* @brief computes the n-th Catalan number and updates the cache.
* @return the n-th Catalan number
*/
value_type get(std::size_t n) {
while (known.size() <= n) {
this->add();
}
return known[n];
}
};
void test_catalan_numbers_up_to_20() {
// data verified with https://oeis.org/A000108/
catalan_numbers cn;
assert(cn.get(0) == 1ULL);
assert(cn.get(1) == 1ULL);
assert(cn.get(2) == 2ULL);
assert(cn.get(3) == 5ULL);
assert(cn.get(4) == 14ULL);
assert(cn.get(5) == 42ULL);
assert(cn.get(6) == 132ULL);
assert(cn.get(7) == 429ULL);
assert(cn.get(8) == 1430ULL);
assert(cn.get(9) == 4862ULL);
assert(cn.get(10) == 16796ULL);
assert(cn.get(11) == 58786ULL);
assert(cn.get(12) == 208012ULL);
assert(cn.get(13) == 742900ULL);
assert(cn.get(14) == 2674440ULL);
assert(cn.get(15) == 9694845ULL);
assert(cn.get(16) == 35357670ULL);
assert(cn.get(17) == 129644790ULL);
assert(cn.get(18) == 477638700ULL);
assert(cn.get(19) == 1767263190ULL);
assert(cn.get(20) == 6564120420ULL);
}
void test_catalan_numbers_25() {
// data verified with https://oeis.org/A000108/
catalan_numbers cn;
assert(cn.get(25) == 4861946401452ULL);
}
int main() {
test_catalan_numbers_up_to_20();
test_catalan_numbers_25();
}
+49
View File
@@ -0,0 +1,49 @@
#include <climits>
#include <iostream>
#include <vector>
using namespace std;
// Function to find the Minimum number of coins required to get Sum S
int findMinCoins(int arr[], int n, int N) {
// dp[i] = no of coins required to get a total of i
std::vector<int> dp(N + 1);
// 0 coins are needed for 0 sum
dp[0] = 0;
for (int i = 1; i <= N; i++) {
// initialize minimum number of coins needed to infinity
dp[i] = INT_MAX;
int res = INT_MAX;
// do for each coin
for (int c = 0; c < n; c++) {
if (i - arr[c] >=
0) // check if coins doesn't become negative by including it
res = dp[i - arr[c]];
// if total can be reached by including current coin c,
// update minimum number of coins needed dp[i]
if (res != INT_MAX)
dp[i] = min(dp[i], res + 1);
}
}
// The Minimum No of Coins Required for N = dp[N]
return dp[N];
}
int main() {
// No of Coins We Have
int arr[] = {1, 2, 3, 4};
int n = sizeof(arr) / sizeof(arr[0]);
// Total Change Required
int N = 15;
cout << "Minimum Number of Coins Required " << findMinCoins(arr, n, N)
<< "\n";
return 0;
}
@@ -0,0 +1,94 @@
/**
* @file
* @brief [Minimum coins](https://leetcode.com/problems/coin-change/) change
* problem is a problem used to find the minimum number of coins required to
* completely reach a target amount.
*
* @details
* This problem can be solved using 2 methods:
* 1. Top down approach
* 2. Bottom up appraoch
* Top down approach involves a vector with all elements initialised to 0.
* It is based on optimal substructure and overlapping subproblems.
* Overall time complexity of coin change problem is O(n*t)
* For example: example 1:-
* Coins: {1,7,10}
* Target:15
* Therfore minimum number of coins required = 3 of denomination 1,7 and 7.
* @author [Divyansh Kushwaha](https://github.com/webdesignbydivyansh)
*/
#include <cassert> // for assert
#include <climits> // for INT_MAX
#include <iostream> // for io operations
#include <vector> // for std::vector
/**
* @namespace dynamic_programming
* @brief Dynamic Programming algorithm
*/
namespace dynamic_programming {
/**
* @namespace mincoins_topdown
* @brief Functions for [minimum coin
* exchange](https://leetcode.com/problems/coin-change/) problem
*/
namespace mincoins_topdown {
/**
* @brief This implementation is for finding minimum number of coins .
* @param T template-type to use any kind of value
* @param n amount to be reached
* @param coins vector of coins
* @param t deontes the number of coins
* @param dp initilised to 0
* @returns minimum number of coins
*/
template <typename T>
int64_t mincoins(const T &n, const std::vector<T> &coins, const int16_t &t,
std::vector<T> dp) {
if (n == 0) {
return 0;
}
if (dp[n] != 0) {
return dp[n];
}
int ans = INT_MAX; // variable to store min coins
for (int i = 0; i < t; i++) {
if (n - coins[i] >= 0) { // if after subtracting the current
// denomination is it greater than 0 or not
int sub = mincoins(n - coins[i], coins, t, dp);
ans = std::min(ans, sub + 1);
}
}
dp[n] = ans;
return dp[n]; // returns minimum number of coins
}
} // namespace mincoins_topdown
} // namespace dynamic_programming
/**
* @brief Test implementations
* @returns void
*/
static void test() {
// example 1: number of coins=3 and minimum coins required=3(7,7,1)
const int64_t n1 = 15;
const int8_t t1 = 3, a1 = 0;
std::cout << "\nTest 1...";
std::vector<int64_t> arr1{1, 7, 10};
std::vector<int64_t> dp1(n1 + 1);
fill(dp1.begin(), dp1.end(), a1);
assert(dynamic_programming::mincoins_topdown::mincoins(n1, arr1, t1, dp1) ==
3);
std::cout << "Passed\n";
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
test(); // execute the test
return 0;
}
+115
View File
@@ -0,0 +1,115 @@
/**
* @file
* @brief Implementation of cutting a rod problem
*
* @details
* Given a rod of length n inches and an array of prices that
* contains prices of all pieces of size<=n. Determine
* the maximum profit obtainable by cutting up the rod and selling
* the pieces.
*
* ### Algorithm
* The idea is to break the given rod into every smaller piece as possible
* and then check profit for each piece, by calculating maximum profit for
* smaller pieces we will build the solution for larger pieces in bottom-up
* manner.
*
* @author [Anmol](https://github.com/Anmol3299)
* @author [Pardeep](https://github.com/Pardeep009)
*/
#include <array>
#include <cassert>
#include <climits>
#include <cstdint>
#include <iostream>
/**
* @namespace dynamic_programming
* @brief Dynamic Programming algorithms
*/
namespace dynamic_programming {
/**
* @namespace cut_rod
* @brief Implementation of cutting a rod problem
*/
namespace cut_rod {
/**
* @brief Cuts the rod in different pieces and
* stores the maximum profit for each piece of the rod.
* @tparam T size of the price array
* @param n size of the rod in inches
* @param price an array of prices that contains prices of all pieces of size<=n
* @return maximum profit obtainable for @param n inch rod.
*/
template <size_t T>
int maxProfitByCuttingRod(const std::array<int, T> &price, const uint64_t &n) {
int *profit =
new int[n + 1]; // profit[i] will hold maximum profit for i inch rod
profit[0] = 0; // if length of rod is zero, then no profit
// outer loop will select size of rod, starting from 1 inch to n inch rod.
// inner loop will evaluate the maximum profit we can get for i inch rod by
// making every possible cut on it and will store it in profit[i].
for (size_t i = 1; i <= n; i++) {
int q = INT_MIN;
for (size_t j = 1; j <= i; j++) {
q = std::max(q, price[j - 1] + profit[i - j]);
}
profit[i] = q;
}
const int16_t ans = profit[n];
delete[] profit;
return ans; // returning maximum profit
}
} // namespace cut_rod
} // namespace dynamic_programming
/**
* @brief Function to test above algorithm
* @returns void
*/
static void test() {
// Test 1
const int16_t n1 = 8; // size of rod
std::array<int32_t, n1> price1 = {1, 2, 4, 6, 8, 45, 21, 9}; // price array
const int64_t max_profit1 =
dynamic_programming::cut_rod::maxProfitByCuttingRod(price1, n1);
const int64_t expected_max_profit1 = 47;
assert(max_profit1 == expected_max_profit1);
std::cout << "Maximum profit with " << n1 << " inch road is " << max_profit1
<< std::endl;
// Test 2
const int16_t n2 = 30; // size of rod
std::array<int32_t, n2> price2 = {
1, 5, 8, 9, 10, 17, 17, 20, 24, 30, // price array
31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50};
const int64_t max_profit2 =
dynamic_programming::cut_rod::maxProfitByCuttingRod(price2, n2);
const int32_t expected_max_profit2 = 90;
assert(max_profit2 == expected_max_profit2);
std::cout << "Maximum profit with " << n2 << " inch road is " << max_profit2
<< std::endl;
// Test 3
const int16_t n3 = 5; // size of rod
std::array<int32_t, n3> price3 = {2, 9, 17, 23, 45}; // price array
const int64_t max_profit3 =
dynamic_programming::cut_rod::maxProfitByCuttingRod(price3, n3);
const int64_t expected_max_profit3 = 45;
assert(max_profit3 == expected_max_profit3);
std::cout << "Maximum profit with " << n3 << " inch road is " << max_profit3
<< std::endl;
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
// Testing
test();
return 0;
}
+86
View File
@@ -0,0 +1,86 @@
/* Given two strings str1 & str2
* and below operations that can
* be performed on str1. Find
* minimum number of edits
* (operations) required to convert
* 'str1' into 'str2'/
* a. Insert
* b. Remove
* c. Replace
* All of the above operations are
* of equal cost
*/
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int min(int x, int y, int z) { return min(min(x, y), z); }
/* A Naive recursive C++ program to find
* minimum number of operations to convert
* str1 to str2.
* O(3^m)
*/
int editDist(string str1, string str2, int m, int n) {
if (m == 0)
return n;
if (n == 0)
return m;
// If last characters are same then continue
// for the rest of them.
if (str1[m - 1] == str2[n - 1])
return editDist(str1, str2, m - 1, n - 1);
// If last not same, then 3 possibilities
// a.Insert b.Remove c. Replace
// Get min of three and continue for rest.
return 1 + min(editDist(str1, str2, m, n - 1),
editDist(str1, str2, m - 1, n),
editDist(str1, str2, m - 1, n - 1));
}
/* A DP based program
* O(m x n)
*/
int editDistDP(string str1, string str2, int m, int n) {
// Create Table for SubProblems
std::vector<std::vector<int> > dp(m + 1, std::vector<int>(n + 1));
// Fill d[][] in bottom up manner
for (int i = 0; i <= m; i++) {
for (int j = 0; j <= n; j++) {
// If str1 empty. Then add all of str2
if (i == 0)
dp[i][j] = j;
// If str2 empty. Then add all of str1
else if (j == 0)
dp[i][j] = i;
// If character same. Recur for remaining
else if (str1[i - 1] == str2[j - 1])
dp[i][j] = dp[i - 1][j - 1];
else
dp[i][j] = 1 + min(dp[i][j - 1], // Insert
dp[i - 1][j], // Remove
dp[i - 1][j - 1] // Replace
);
}
}
return dp[m][n];
}
int main() {
string str1 = "sunday";
string str2 = "saturday";
cout << editDist(str1, str2, str1.length(), str2.length()) << endl;
cout << editDistDP(str1, str2, str1.length(), str2.length()) << endl;
return 0;
}
@@ -0,0 +1,48 @@
/* Function to get minimun number of trials needed
* in worst case with n eggs and k floors
*/
#include <climits>
#include <iostream>
#include <vector>
using namespace std;
int eggDrop(int n, int k) {
std::vector<std::vector<int> > eggFloor(n + 1, std::vector<int>(k + 1));
int result;
for (int i = 1; i <= n; i++) {
eggFloor[i][1] = 1; // n eggs..1 Floor
eggFloor[i][0] = 0; // n eggs..0 Floor
}
// Only one egg available
for (int j = 1; j <= k; j++) {
eggFloor[1][j] = j;
}
for (int i = 2; i <= n; i++) {
for (int j = 2; j <= k; j++) {
eggFloor[i][j] = INT_MAX;
for (int x = 1; x <= j; x++) {
// 1+max(eggBreak[one less egg, lower floors],
// eggDoesntBreak[same # of eggs, upper floors]);
result = 1 + max(eggFloor[i - 1][x - 1], eggFloor[i][j - x]);
if (result < eggFloor[i][j])
eggFloor[i][j] = result;
}
}
}
return eggFloor[n][k];
}
int main() {
int n, k;
cout << "Enter number of eggs and floors: ";
cin >> n >> k;
cout << "Minimum number of trials in worst case: " << eggDrop(n, k) << endl;
return 0;
}
@@ -0,0 +1,21 @@
#include <iostream>
using namespace std;
int fib(int n) {
int res[3];
res[0] = 0;
res[1] = 1;
for (int i = 2; i <= n; i++) {
res[2] = res[1] + res[0];
res[0] = res[1];
res[1] = res[2];
}
return res[1];
}
int main() {
int n;
cout << "Enter n: ";
cin >> n;
cout << "Fibonacci number is ";
cout << fib(n) << endl;
return 0;
}
+110
View File
@@ -0,0 +1,110 @@
#include <climits>
#include <cstddef>
#include <iostream>
#include <vector>
using std::cin;
using std::cout;
using std::endl;
// Wrapper class for storing a graph
class Graph {
public:
int vertexNum;
int **edges;
// Constructs a graph with V vertices and E edges
Graph(int V) {
this->vertexNum = V;
this->edges = new int *[V];
for (int i = 0; i < V; i++) {
this->edges[i] = new int[V];
for (int j = 0; j < V; j++) this->edges[i][j] = INT_MAX;
this->edges[i][i] = 0;
}
}
~Graph() {
for (int i = 0; i < vertexNum; i++) {
delete[] edges[i];
}
delete[] edges;
}
// Adds the given edge to the graph
void addEdge(int src, int dst, int weight) {
this->edges[src][dst] = weight;
}
};
// Utility function to print distances
void print(const std::vector<int>& dist, int V) {
cout << "\nThe Distance matrix for Floyd - Warshall" << endl;
for (int i = 0; i < V; i++) {
for (int j = 0; j < V; j++) {
if (dist[i * V + j] != INT_MAX)
cout << dist[i * V + j] << "\t";
else
cout << "INF"
<< "\t";
}
cout << endl;
}
}
// The main function that finds the shortest path from a vertex
// to all other vertices using Floyd-Warshall Algorithm.
void FloydWarshall(Graph graph) {
std::size_t V = graph.vertexNum;
std::vector<std::vector<int> > dist(V, std::vector<int>(V));
// Initialise distance array
for (int i = 0; i < V; i++)
for (int j = 0; j < V; j++) dist[i][j] = graph.edges[i][j];
// Calculate distances
for (int k = 0; k < V; k++)
// Choose an intermediate vertex
for (int i = 0; i < V; i++)
// Choose a source vertex for given intermediate
for (int j = 0; j < V; j++)
// Choose a destination vertex for above source vertex
if (dist[i][k] != INT_MAX && dist[k][j] != INT_MAX &&
dist[i][k] + dist[k][j] < dist[i][j])
// If the distance through intermediate vertex is less than
// direct edge then update value in distance array
dist[i][j] = dist[i][k] + dist[k][j];
// Convert 2d array to 1d array for print
std::vector<int> dist1d(V * V);
for (int i = 0; i < V; i++)
for (int j = 0; j < V; j++) dist1d[i * V + j] = dist[i][j];
print(dist1d, V);
}
// Driver Function
int main() {
int V, E;
int src, dst, weight;
cout << "Enter number of vertices: ";
cin >> V;
cout << "Enter number of edges: ";
cin >> E;
Graph G(V);
for (int i = 0; i < E; i++) {
cout << "\nEdge " << i + 1 << "\nEnter source: ";
cin >> src;
cout << "Enter destination: ";
cin >> dst;
cout << "Enter weight: ";
cin >> weight;
G.addEdge(src, dst, weight);
}
FloydWarshall(G);
return 0;
}
+114
View File
@@ -0,0 +1,114 @@
/**
* @file
* @brief Implementation of [House Robber
* Problem](https://labuladong.gitbook.io/algo-en/i.-dynamic-programming/houserobber)
* algorithm
* @details
* Solution of House robber problem uses a dynamic programming concept that
* works in \f$O(n)\f$ time and works in \f$O(1)\f$ space.
* @author [Swastika Gupta](https://github.com/Swastyy)
*/
#include <cassert> /// for assert
#include <climits> /// for std::max
#include <cstdint> /// for std::uint32_t
#include <iostream> /// for io operations
#include <vector> /// for std::vector
/**
* @namespace dynamic_programming
* @brief Dynamic Programming algorithms
*/
namespace dynamic_programming {
/**
* @namespace house_robber
* @brief Functions for the [House
* Robber](https://labuladong.gitbook.io/algo-en/i.-dynamic-programming/houserobber)
* algorithm
*/
namespace house_robber {
/**
* @brief The main function that implements the House Robber algorithm using
* dynamic programming
* @param money array containing money in the ith house
* @param n size of array
* @returns maximum amount of money that can be robbed
*/
std::uint32_t houseRobber(const std::vector<uint32_t> &money,
const uint32_t &n) {
if (n == 0) { // if there is no house
return 0;
}
if (n == 1) { // if there is only one house
return money[0];
}
if (n == 2) { // if there are two houses, one with the maximum amount of
// money will be robbed
return std::max(money[0], money[1]);
}
uint32_t max_value = 0; // contains maximum stolen value at the end
uint32_t value1 = money[0];
uint32_t value2 = std::max(money[0], money[1]);
for (uint32_t i = 2; i < n; i++) {
max_value = std::max(money[i] + value1, value2);
value1 = value2;
value2 = max_value;
}
return max_value;
}
} // namespace house_robber
} // namespace dynamic_programming
/**
* @brief Self-test implementations
* @returns void
*/
static void test() {
// Test 1
// [1, 2, 3, 1] return 4
std::vector<uint32_t> array1 = {1, 2, 3, 1};
std::cout << "Test 1... ";
assert(
dynamic_programming::house_robber::houseRobber(array1, array1.size()) ==
4); // here the two non-adjacent houses that are robbed are first and
// third with total sum money as 4
std::cout << "passed" << std::endl;
// Test 2
// [6, 7, 1, 3, 8, 2, 4] return 19
std::vector<uint32_t> array2 = {6, 7, 1, 3, 8, 2, 4};
std::cout << "Test 2... ";
assert(
dynamic_programming::house_robber::houseRobber(array2, array2.size()) ==
19); // here the four non-adjacent houses that are robbed are first,
// third, fifth and seventh with total sum money as 19
std::cout << "passed" << std::endl;
// Test 3
// [] return 0
std::vector<uint32_t> array3 = {};
std::cout << "Test 3... ";
assert(
dynamic_programming::house_robber::houseRobber(array3, array3.size()) ==
0); // since there is no house no money can be robbed
std::cout << "passed" << std::endl;
// Test 4
// [2,7,9,3,1] return 12
std::vector<uint32_t> array4 = {2, 7, 9, 3, 1};
std::cout << "Test 4... ";
assert(
dynamic_programming::house_robber::houseRobber(array4, array4.size()) ==
12); // here the three non-adjacent houses that are robbed are first,
// third and fifth with total sum money as 12
std::cout << "passed" << std::endl;
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
test(); // run self-test implementations
return 0;
}
+74
View File
@@ -0,0 +1,74 @@
/**
* @file
* @brief Implementation of [Kadane
* Algorithm](https://en.wikipedia.org/wiki/Kadane%27s_algorithm)
*
* @details
* Kadane algorithm is used to find the maximum sum subarray in an array and
* maximum sum subarray problem is the task of finding a contiguous subarray
* with the largest sum
*
* ### Algorithm
* The simple idea of the algorithm is to search for all positive
* contiguous segments of the array and keep track of maximum sum contiguous
* segment among all positive segments(curr_sum is used for this)
* Each time we get a positive sum we compare it with max_sum and update max_sum
* if it is greater than curr_sum
*
* @author [Ayush Singh](https://github.com/ayush523)
*/
#include <array>
#include <climits>
#include <iostream>
/**
* @namespace dynamic_programming
* @brief Dynamic Programming algorithms
*/
namespace dynamic_programming {
/**
* @namespace kadane
* @brief Functions for
* [Kadane](https://en.wikipedia.org/wiki/Kadane%27s_algorithm) algorithm.
*/
namespace kadane {
/**
* @brief maxSubArray function is used to calculate the maximum sum subarray
* and returns the value of maximum sum which is stored in the variable max_sum
* @tparam N number of array size
* @param n array where numbers are saved
* @returns the value of maximum subarray sum
*/
template <size_t N>
int maxSubArray(const std::array<int, N> &n) {
int curr_sum =
0; // declaring a variable named as curr_sum and initialized it to 0
int max_sum = INT_MIN; // Initialized max_sum to INT_MIN
for (int i : n) { // for loop to iterate over the elements of the array
curr_sum += n[i];
max_sum = std::max(max_sum, curr_sum); // getting the maximum value
curr_sum = std::max(curr_sum, 0); // updating the value of curr_sum
}
return max_sum; // returning the value of max_sum
}
} // namespace kadane
} // namespace dynamic_programming
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
const int N = 5;
std::array<int, N> n{}; // declaring array
// taking values of elements from user
for (int i = 0; i < n.size(); i++) {
std::cout << "Enter value of n[" << i << "]"
<< "\n";
std::cin >> n[i];
}
int max_sum = dynamic_programming::kadane::maxSubArray<N>(
n); // calling maxSubArray function
std::cout << "Maximum subarray sum is " << max_sum; // Printing the answer
return 0;
}
@@ -0,0 +1,159 @@
/**
* @file
* @brief contains the definition of the function ::longest_common_string_length
* @details
* the function ::longest_common_string_length computes the length
* of the longest common string which can be created of two input strings
* by removing characters from them
*
* @author [Nikhil Arora](https://github.com/nikhilarora068)
* @author [Piotr Idzik](https://github.com/vil02)
*/
#include <cassert> /// for assert
#include <iostream> /// for std::cout
#include <string> /// for std::string
#include <utility> /// for std::move
#include <vector> /// for std::vector
/**
* @brief computes the length of the longest common string created from input
* strings
* @details has O(str_a.size()*str_b.size()) time and memory complexity
* @param string_a first input string
* @param string_b second input string
* @returns the length of the longest common string which can be strated from
* str_a and str_b
*/
std::size_t longest_common_string_length(const std::string& string_a,
const std::string& string_b) {
const auto size_a = string_a.size();
const auto size_b = string_b.size();
std::vector<std::vector<std::size_t>> sub_sols(
size_a + 1, std::vector<std::size_t>(size_b + 1, 0));
const auto limit = static_cast<std::size_t>(-1);
for (std::size_t pos_a = size_a - 1; pos_a != limit; --pos_a) {
for (std::size_t pos_b = size_b - 1; pos_b != limit; --pos_b) {
if (string_a[pos_a] == string_b[pos_b]) {
sub_sols[pos_a][pos_b] = 1 + sub_sols[pos_a + 1][pos_b + 1];
} else {
sub_sols[pos_a][pos_b] = std::max(sub_sols[pos_a + 1][pos_b],
sub_sols[pos_a][pos_b + 1]);
}
}
}
return sub_sols[0][0];
}
/**
* @brief represents single example inputs and expected output of the function
* ::longest_common_string_length
*/
struct TestCase {
const std::string string_a;
const std::string string_b;
const std::size_t common_string_len;
TestCase(std::string string_a, std::string string_b,
const std::size_t in_common_string_len)
: string_a(std::move(string_a)),
string_b(std::move(string_b)),
common_string_len(in_common_string_len) {}
};
/**
* @return example data used in the tests of ::longest_common_string_length
*/
std::vector<TestCase> get_test_cases() {
return {TestCase("", "", 0),
TestCase("ab", "ab", 2),
TestCase("ab", "ba", 1),
TestCase("", "xyz", 0),
TestCase("abcde", "ace", 3),
TestCase("BADANA", "ANADA", 3),
TestCase("BADANA", "CANADAS", 3),
TestCase("a1a234a5aaaa6", "A1AAAA234AAA56AAAAA", 6),
TestCase("123x", "123", 3),
TestCase("12x3x", "123", 3),
TestCase("1x2x3x", "123", 3),
TestCase("x1x2x3x", "123", 3),
TestCase("x12x3x", "123", 3)};
}
/**
* @brief checks the function ::longest_common_string_length agains example data
* @param test_cases list of test cases
* @tparam type representing a list of test cases
*/
template <typename TestCases>
static void test_longest_common_string_length(const TestCases& test_cases) {
for (const auto& cur_tc : test_cases) {
assert(longest_common_string_length(cur_tc.string_a, cur_tc.string_b) ==
cur_tc.common_string_len);
}
}
/**
* @brief checks if the function ::longest_common_string_length returns the same
* result when its argument are flipped
* @param test_cases list of test cases
* @tparam type representing a list of test cases
*/
template <typename TestCases>
static void test_longest_common_string_length_is_symmetric(
const TestCases& test_cases) {
for (const auto& cur_tc : test_cases) {
assert(longest_common_string_length(cur_tc.string_b, cur_tc.string_a) ==
cur_tc.common_string_len);
}
}
/**
* @brief reverses a given string
* @param in_str input string
* @return the string in which the characters appear in the reversed order as in
* in_str
*/
std::string reverse_str(const std::string& in_str) {
return {in_str.rbegin(), in_str.rend()};
}
/**
* @brief checks if the function ::longest_common_string_length returns the same
* result when its inputs are reversed
* @param test_cases list of test cases
* @tparam type representing a list of test cases
*/
template <typename TestCases>
static void test_longest_common_string_length_for_reversed_inputs(
const TestCases& test_cases) {
for (const auto& cur_tc : test_cases) {
assert(longest_common_string_length(reverse_str(cur_tc.string_a),
reverse_str(cur_tc.string_b)) ==
cur_tc.common_string_len);
}
}
/**
* @brief runs all tests for ::longest_common_string_length funcion
*/
static void tests() {
const auto test_cases = get_test_cases();
assert(test_cases.size() > 0);
test_longest_common_string_length(test_cases);
test_longest_common_string_length_is_symmetric(test_cases);
test_longest_common_string_length_for_reversed_inputs(test_cases);
std::cout << "All tests have successfully passed!\n";
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
tests();
return 0;
}
@@ -0,0 +1,66 @@
// Longest common subsequence - Dynamic Programming
#include <iostream>
#include <vector>
using namespace std;
void Print(int trace[20][20], int m, int n, string a) {
if (m == 0 || n == 0) {
return;
}
if (trace[m][n] == 1) {
Print(trace, m - 1, n - 1, a);
cout << a[m - 1];
} else if (trace[m][n] == 2) {
Print(trace, m - 1, n, a);
} else if (trace[m][n] == 3) {
Print(trace, m, n - 1, a);
}
}
int lcs(string a, string b) {
int m = a.length(), n = b.length();
std::vector<std::vector<int> > res(m + 1, std::vector<int>(n + 1));
int trace[20][20];
// fills up the arrays with zeros.
for (int i = 0; i < m + 1; i++) {
for (int j = 0; j < n + 1; j++) {
res[i][j] = 0;
trace[i][j] = 0;
}
}
for (int i = 0; i < m + 1; ++i) {
for (int j = 0; j < n + 1; ++j) {
if (i == 0 || j == 0) {
res[i][j] = 0;
trace[i][j] = 0;
}
else if (a[i - 1] == b[j - 1]) {
res[i][j] = 1 + res[i - 1][j - 1];
trace[i][j] = 1; // 1 means trace the matrix in upper left
// diagonal direction.
} else {
if (res[i - 1][j] > res[i][j - 1]) {
res[i][j] = res[i - 1][j];
trace[i][j] =
2; // 2 means trace the matrix in upwards direction.
} else {
res[i][j] = res[i][j - 1];
trace[i][j] =
3; // means trace the matrix in left direction.
}
}
}
}
Print(trace, m, n, a);
return res[m][n];
}
int main() {
string a, b;
cin >> a >> b;
cout << lcs(a, b);
return 0;
}
@@ -0,0 +1,97 @@
/**
* @file
* @brief Calculate the length of the [longest increasing
* subsequence](https://en.wikipedia.org/wiki/Longest_increasing_subsequence) in
* an array
*
* @details
* In computer science, the longest increasing subsequence problem is to find a
* subsequence of a given sequence in which the subsequence's elements are in
* sorted order, lowest to highest, and in which the subsequence is as long as
* possible. This subsequence is not necessarily contiguous, or unique. Longest
* increasing subsequences are studied in the context of various disciplines
* related to mathematics, including algorithmics, random matrix theory,
* representation theory, and physics. The longest increasing subsequence
* problem is solvable in time O(n log n), where n denotes the length of the
* input sequence.
*
* @author [Krishna Vedala](https://github.com/kvedala)
* @author [David Leal](https://github.com/Panquesito7)
*/
#include <cassert> /// for assert
#include <climits> /// for std::max
#include <cstdint> /// for std::uint64_t
#include <iostream> /// for IO operations
#include <vector> /// for std::vector
/**
* @namespace dynamic_programming
* @brief Dynamic Programming algorithms
*/
namespace dynamic_programming {
/**
* @brief Calculate the longest increasing subsequence for the specified numbers
* @param a the array used to calculate the longest increasing subsequence
* @param n the size used for the arrays
* @returns the length of the longest increasing
* subsequence in the `a` array of size `n`
*/
uint64_t LIS(const std::vector<uint64_t> &a, const uint32_t &n) {
std::vector<int> lis(n);
for (int i = 0; i < n; ++i) {
lis[i] = 1;
}
for (int i = 0; i < n; ++i) {
for (int j = 0; j < i; ++j) {
if (a[i] > a[j] && lis[i] < lis[j] + 1) {
lis[i] = lis[j] + 1;
}
}
}
int res = 0;
for (int i = 0; i < n; ++i) {
res = std::max(res, lis[i]);
}
return res;
}
} // namespace dynamic_programming
/**
* @brief Self-test implementations
* @returns void
*/
static void test() {
std::vector<uint64_t> a = {15, 21, 2, 3, 4, 5, 8, 4, 1, 1};
uint32_t n = a.size();
uint32_t result = dynamic_programming::LIS(a, n);
assert(result ==
5); ///< The longest increasing subsequence is `{2,3,4,5,8}`
std::cout << "Self-test implementations passed!" << std::endl;
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
uint32_t n = 0;
std::cout << "Enter size of array: ";
std::cin >> n;
std::vector<uint64_t> a(n);
std::cout << "Enter array elements: ";
for (int i = 0; i < n; ++i) {
std::cin >> a[i];
}
std::cout << "\nThe result is: " << dynamic_programming::LIS(a, n)
<< std::endl;
test(); // run self-test implementations
return 0;
}
@@ -0,0 +1,43 @@
// Program to calculate length of longest increasing subsequence in an array
// in O(n log n)
// tested on : https://cses.fi/problemset/task/1145/
#include <iostream>
#include <set>
#include <vector>
using namespace std;
int LIS(const std::vector<int>& arr, int n) {
set<int> active; // The current built LIS.
active.insert(arr[0]);
// Loop through every element.
for (int i = 1; i < n; ++i) {
auto get = active.lower_bound(arr[i]);
if (get == active.end()) {
active.insert(arr[i]);
} // current element is the greatest so LIS increases by 1.
else {
int val = *get; // we find the position where arr[i] will be in the
// LIS. If it is in the LIS already we do nothing
if (val > arr[i]) {
// else we remove the bigger element and add a smaller element
// (which is arr[i]) and continue;
active.erase(get);
active.insert(arr[i]);
}
}
}
return active.size(); // size of the LIS.
}
int main() {
int n;
cout << "Enter size of array: ";
cin >> n;
std::vector<int> a(n);
cout << "Enter array elements: ";
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
cout << LIS(a, n) << endl;
return 0;
}
@@ -0,0 +1,100 @@
/**
* @file
* @brief Program to find the [Longest Palindormic
* Subsequence](https://www.geeksforgeeks.org/longest-palindromic-subsequence-dp-12/) of a string
*
* @details
* [Palindrome](https://en.wikipedia.org/wiki/Palindrome) string sequence of
* characters which reads the same backward as forward
* [Subsequence](https://en.wikipedia.org/wiki/Subsequence) is a sequence that
* can be derived from another sequence by deleting some or no elements without
* changing the order of the remaining elements.
* @author [Anjali Jha](https://github.com/anjali1903)
*/
#include <cassert> /// for assert
#include <string> /// for std::string
#include <vector> /// for std::vector
/**
* @namespace
* @brief Dynamic Programming algorithms
*/
namespace dynamic_programming {
/**
* @brief Function that returns the longest palindromic
* subsequence of a string
* @param a string whose longest palindromic subsequence is to be found
* @returns longest palindromic subsequence of the string
*/
std::string lps(const std::string& a) {
const auto b = std::string(a.rbegin(), a.rend());
const auto m = a.length();
using ind_type = std::string::size_type;
std::vector<std::vector<ind_type> > res(m + 1,
std::vector<ind_type>(m + 1));
// Finding the length of the longest
// palindromic subsequence and storing
// in a 2D array in bottoms-up manner
for (ind_type i = 0; i <= m; i++) {
for (ind_type j = 0; j <= m; j++) {
if (i == 0 || j == 0) {
res[i][j] = 0;
} else if (a[i - 1] == b[j - 1]) {
res[i][j] = res[i - 1][j - 1] + 1;
} else {
res[i][j] = std::max(res[i - 1][j], res[i][j - 1]);
}
}
}
// Length of longest palindromic subsequence
auto idx = res[m][m];
// Creating string of index+1 length
std::string ans(idx, '\0');
ind_type i = m, j = m;
// starting from right-most bottom-most corner
// and storing them one by one in ans
while (i > 0 && j > 0) {
// if current characters in a and b are same
// then it is a part of the ans
if (a[i - 1] == b[j - 1]) {
ans[idx - 1] = a[i - 1];
i--;
j--;
idx--;
}
// If they are not same, find the larger of the
// two and move in that direction
else if (res[i - 1][j] > res[i][j - 1]) {
i--;
} else {
j--;
}
}
return ans;
}
} // namespace dynamic_programming
/**
* @brief Self-test implementations
* @returns void
*/
static void test() {
assert(dynamic_programming::lps("radar") == "radar");
assert(dynamic_programming::lps("abbcbaa") == "abcba");
assert(dynamic_programming::lps("bbbab") == "bbbb");
assert(dynamic_programming::lps("") == "");
}
/**
* @brief Main Function
* @returns 0 on exit
*/
int main() {
test(); // execute the tests
return 0;
}
@@ -0,0 +1,61 @@
#include <climits>
#include <iostream>
using namespace std;
#define MAX 10
// dp table to store the solution for already computed sub problems
int dp[MAX][MAX];
// Function to find the most efficient way to multiply the given sequence of
// matrices
int MatrixChainMultiplication(int dim[], int i, int j) {
// base case: one matrix
if (j <= i + 1)
return 0;
// stores minimum number of scalar multiplications (i.e., cost)
// needed to compute the matrix M[i+1]...M[j] = M[i..j]
int min = INT_MAX;
// if dp[i][j] is not calculated (calculate it!!)
if (dp[i][j] == 0) {
// take the minimum over each possible position at which the
// sequence of matrices can be split
for (int k = i + 1; k <= j - 1; k++) {
// recur for M[i+1]..M[k] to get a i x k matrix
int cost = MatrixChainMultiplication(dim, i, k);
// recur for M[k+1]..M[j] to get a k x j matrix
cost += MatrixChainMultiplication(dim, k, j);
// cost to multiply two (i x k) and (k x j) matrix
cost += dim[i] * dim[k] * dim[j];
if (cost < min)
min = cost; // store the minimum cost
}
dp[i][j] = min;
}
// return min cost to multiply M[j+1]..M[j]
return dp[i][j];
}
// main function
int main() {
// Matrix i has Dimensions dim[i-1] & dim[i] for i=1..n
// input is 10 x 30 matrix, 30 x 5 matrix, 5 x 60 matrix
int dim[] = {10, 30, 5, 60};
int n = sizeof(dim) / sizeof(dim[0]);
// Function Calling: MatrixChainMultiplications(dimensions_array, starting,
// ending);
cout << "Minimum cost is " << MatrixChainMultiplication(dim, 0, n - 1)
<< "\n";
return 0;
}
@@ -0,0 +1,87 @@
/**
* @file
* @brief C++ program for maximum contiguous circular sum problem using [Kadane's Algorithm](https://en.wikipedia.org/wiki/Maximum_subarray_problem)
* @details
* The idea is to modify Kadanes algorithm to find a minimum contiguous subarray sum and the maximum contiguous subarray sum,
* then check for the maximum value between the max_value and the value left after subtracting min_value from the total sum.
* For more information, check [Geeks For Geeks](https://www.geeksforgeeks.org/maximum-contiguous-circular-sum/) explanation page.
*/
#include <cassert> /// for assert
#include <iostream> /// for IO operations
#include <vector> /// for std::vector
/**
* @namespace dynamic_programming
* @brief Dynamic Programming algorithms
*/
namespace dynamic_programming {
/**
* @brief returns the maximum contiguous circular sum of an array
*
* @param arr is the array/vector
* @return int which is the maximum sum
*/
int maxCircularSum(std::vector<int>& arr)
{
// Edge Case
if (arr.size() == 1)
return arr[0];
// Sum variable which stores total sum of the array.
int sum = 0;
for (int i = 0; i < arr.size(); i++) {
sum += arr[i];
}
// Every variable stores first value of the array.
int current_max = arr[0], max_so_far = arr[0], current_min = arr[0], min_so_far = arr[0];
// Concept of Kadane's Algorithm
for (int i = 1; i < arr.size(); i++) {
// Kadane's Algorithm to find Maximum subarray sum.
current_max = std::max(current_max + arr[i], arr[i]);
max_so_far = std::max(max_so_far, current_max);
// Kadane's Algorithm to find Minimum subarray sum.
current_min = std::min(current_min + arr[i], arr[i]);
min_so_far = std::min(min_so_far, current_min);
}
if (min_so_far == sum)
return max_so_far;
// Return the maximum value
return std::max(max_so_far, sum - min_so_far);
}
} // namespace dynamic_programming
/**
* @brief Self-test implementation
* @returns void
*/
static void test() {
// Description of the test
// Input: arr[] = {8, -8, 9, -9, 10, -11, 12}
// Output: 22
// Explanation: Subarray 12, 8, -8, 9, -9, 10 gives the maximum sum, that is 22.
std::vector<int> arr = {8, -8, 9, -9, 10, -11, 12};
assert(dynamic_programming::maxCircularSum(arr) == 22); // this ensures that the algorithm works as expected
arr = {8, -8, 10, -9, 10, -11, 12};
assert(dynamic_programming::maxCircularSum(arr) == 23);
std::cout << "All tests have successfully passed!\n";
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
test(); // run self-test implementations
return 0;
}
@@ -0,0 +1,174 @@
/**
* @file
* @brief Implementation of [Minimum Edit
* Distance](https://en.wikipedia.org/wiki/Edit_distance) using Dynamic
* Programing
*
* @details
*
* Given two strings str1 & str2 and we have to calculate the minimum
* number of operations (Insert, Remove, Replace) required to convert
* str1 to str2.
*
* ### Algorithm
*
* We will solve this problem using Naive recursion. But as we are
* approaching with a DP solution. So, we will take a DP array to
* store the solution of all sub-problems so that we don't have to
* perform recursion again and again. Now to solve the problem, We
* can traverse all characters from either right side of the strings
* or left side. Suppose we will do it from the right side. So, there
* are two possibilities for every pair of characters being traversed.
* 1. If the last characters of two strings are the same, Ignore
* the characters and get the count for the remaining string.
* So, we get the solution for lengths m-1 and n-1 in a DP array.
*
* 2. Else, (If last characters are not the same), we will consider all
* three operations (Insert, Remove, Replace) on the last character of
* the first string and compute the minimum cost for all three operations
* and take the minimum of three values in the DP array.
* For Insert: Recur for m and n-1
* For Remove: Recur for for m-1 and n
* For Replace: Recur for for m-1 and n-1
*
* @author [Nirjas Jakilim](github.com/nirzak)
*/
#include <cassert> /// for assert
#include <cstdint> /// for std::uint64_t
#include <iostream> /// for IO operations
#include <vector> /// for std::vector
/**
* @namespace dynamic_programming
* @brief Dynamic Programming algorithms
*/
namespace dynamic_programming {
/**
* @namespace Minimum Edit Distance
* @brief Implementation of [Minimum Edit
* Distance](https://en.wikipedia.org/wiki/Edit_distance) algorithm
*/
namespace minimum_edit_distance {
/**
* @brief Takes input of the cost of
* three operations: Insert, Replace and Delete
* and return the minimum cost among them.
* @param x used to pass minimum cost of Insert operations
* @param y used to pass minimum cost of Replace operations
* @param z used to pass minimum cost of Delete operations
* @returns x if `x` is the minimum value
* @returns y if `y` is the minimum value
* @returns z if `z` is the minimum value
*/
uint64_t min(uint64_t x, uint64_t y, uint64_t z) {
if (x <= y && x <= z) {
return x; /// returns x, if x is the minimum value
}
if (y <= x && y <= z) {
return y; /// returns y, if y is the minimum value
} else {
return z; /// returns z if z is the minimum value
}
}
/**
* @brief Calculates and stores the result
* of all the sub-problems, so that we don't have to recur to compute
* the minimum cost of a particular operation if it is already
* computed and stored in the `dp` vector.
* @param dp vector to store the computed minimum costs
* @param str1 to pass the 1st string
* @param str2 to pass the 2nd string
* @param m the length of str1
* @param n the length of str2
* @returns dp[m][n] the minimum cost of operations
* needed to convert str1 to str2
*/
uint64_t editDistDP(std::string str1, std::string str2, uint64_t m,
uint64_t n) {
/// Create a table to store results of subproblems
std::vector<std::vector<uint64_t>> dp(
m + 1,
std::vector<uint64_t>(
n +
1)); /// creasting 2D vector dp to store the results of subproblems
/// Fill d[][] in bottom up manner
for (uint64_t i = 0; i <= m; i++) {
for (uint64_t j = 0; j <= n; j++) {
/// If first string is empty, only option is to
/// insert all characters of second string
if (i == 0) {
dp[i][j] = j; /// Minimum operations = j
}
/// If second string is empty, only option is to
/// remove all characters of second string
else if (j == 0) {
dp[i][j] = i; /// Minimum operations = i
}
/// If last characters are same, ignore last char
/// and recur for remaining string
else if (str1[i - 1] == str2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1];
}
/// If the last character is different, consider all
/// possibilities and find the minimum
else {
dp[i][j] = 1 + min(dp[i][j - 1], // Insert
dp[i - 1][j], // Remove
dp[i - 1][j - 1]); // Replace
}
}
}
return dp[m][n]; /// returning the minimum cost of operations needed to
/// convert str1 to str2
}
} // namespace minimum_edit_distance
} // namespace dynamic_programming
/**
* @brief Self-test implementations
* @returns void
*/
static void test() {
// 1st test
std::string str1 = "INTENTION"; // Sample input of 1st string
std::string str2 = "EXECUTION"; // Sample input of 2nd string
uint64_t expected_output1 = 5; // Expected minimum cost
uint64_t output1 = dynamic_programming::minimum_edit_distance::editDistDP(
str1, str2, str1.length(),
str2.length()); // calling the editDistDP function and storing the
// result on output1
assert(output1 ==
expected_output1); // comparing the output with the expected output
std::cout << "Minimum Number of Operations Required: " << output1
<< std::endl;
// 2nd test
std::string str3 = "SATURDAY";
std::string str4 = "SUNDAY";
uint64_t expected_output2 = 3;
uint64_t output2 = dynamic_programming::minimum_edit_distance::editDistDP(
str3, str4, str3.length(), str4.length());
assert(output2 == expected_output2);
std::cout << "Minimum Number of Operations Required: " << output2
<< std::endl;
}
/**
* @brief main function
* @returns 0 on exit
*/
int main() {
test(); // run self-test implementations
return 0;
}
@@ -0,0 +1,131 @@
/**
* @file
* @brief Implements [Palindrome
* Partitioning](https://leetcode.com/problems/palindrome-partitioning-ii/)
* algorithm, giving you the minimum number of partitions you need to make
*
* @details
* palindrome partitioning uses dynamic programming and goes to all the possible
* partitions to find the minimum you are given a string and you need to give
* minimum number of partitions needed to divide it into a number of palindromes
* [Palindrome Partitioning]
* (https://www.geeksforgeeks.org/palindrome-partitioning-dp-17/) overall time
* complexity O(n^2) For example: example 1:- String : "nitik" Output : 2 => "n
* | iti | k" For example: example 2:- String : "ababbbabbababa" Output : 3 =>
* "aba | b | bbabb | ababa"
* @author [Sujay Kaushik] (https://github.com/sujaykaushik008)
*/
#include <algorithm> // for std::min
#include <cassert> // for std::assert
#include <climits> // for INT_MAX
#include <iostream> // for io operations
#include <vector> // for std::vector
/**
* @namespace dynamic_programming
* @brief Dynamic Programming algorithms
*/
namespace dynamic_programming {
/**
* @namespace palindrome_partitioning
* @brief Functions for [Palindrome
* Partitioning](https://leetcode.com/problems/palindrome-partitioning-ii/)
* algorithm
*/
namespace palindrome_partitioning {
/**
* Function implementing palindrome partitioning algorithm using lookup table
* method.
* @param str input string
* @returns minimum number of partitions
*/
int pal_part(const std::string &str) {
int n = str.size();
// creating lookup table for minimum number of cuts
std::vector<std::vector<int> > cuts(n, std::vector<int>(n, 0));
// creating lookup table for palindrome checking
std::vector<std::vector<bool> > is_palindrome(n,
std::vector<bool>(n, false));
// initialization
for (int i = 0; i < n; i++) {
is_palindrome[i][i] = true;
cuts[i][i] = 0;
}
for (int len = 2; len <= n; len++) {
for (int start_index = 0; start_index < n - len + 1; start_index++) {
int end_index = start_index + len - 1;
if (len == 2) {
is_palindrome[start_index][end_index] =
(str[start_index] == str[end_index]);
} else {
is_palindrome[start_index][end_index] =
(str[start_index] == str[end_index]) &&
is_palindrome[start_index + 1][end_index - 1];
}
if (is_palindrome[start_index][end_index]) {
cuts[start_index][end_index] = 0;
} else {
cuts[start_index][end_index] = INT_MAX;
for (int partition = start_index; partition <= end_index - 1;
partition++) {
cuts[start_index][end_index] =
std::min(cuts[start_index][end_index],
cuts[start_index][partition] +
cuts[partition + 1][end_index] + 1);
}
}
}
}
return cuts[0][n - 1];
}
} // namespace palindrome_partitioning
} // namespace dynamic_programming
/**
* @brief Test Function
* @return void
*/
static void test() {
// custom input vector
std::vector<std::string> custom_input{"nitik", "ababbbabbababa", "abdc"};
// calculated output vector by pal_part Function
std::vector<int> calculated_output(3);
for (int i = 0; i < 3; i++) {
calculated_output[i] =
dynamic_programming::palindrome_partitioning::pal_part(
custom_input[i]);
}
// expected output vector
std::vector<int> expected_output{2, 3, 3};
// Testing implementation via assert function
// It will throw error if any of the expected test fails
// Else it will give nothing
for (int i = 0; i < 3; i++) {
assert(expected_output[i] == calculated_output[i]);
}
std::cout << "All tests passed successfully!\n";
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
test(); // execute the test
return 0;
}
+106
View File
@@ -0,0 +1,106 @@
/******************************************************************************
* @file
* @brief Implementation of the [Partition
* Problem](https://en.wikipedia.org/wiki/Partition_problem )
* @details
* The partition problem, or number partitioning, is the task of deciding
* whether a given multiset S of positive integers can be partitioned into two
* subsets S1 and S2 such that the sum of the numbers in S1 equals the sum of
* the numbers in S2. Although the partition problem is NP-complete, there is a
* pseudo-polynomial time dynamic programming solution, and there are heuristics
* that solve the problem in many instances, either optimally or approximately.
* For this reason, it has been called "the easiest hard problem".
*
* The worst case time complexity of Jarviss Algorithm is O(n^2). Using
* Grahams scan algorithm, we can find Convex Hull in O(nLogn) time.
*
* ### Implementation
*
* Step 1
* Calculate sum of the array. If sum is odd, there can not be two subsets with
* equal sum, so return false.
*
* Step 2
* If sum of array elements is even, calculate sum/2 and find a subset of array
* with sum equal to sum/2.
*
* @author [Lajat Manekar](https://github.com/Lazeeez)
*
*******************************************************************************/
#include <cassert> /// for assert
#include <cstdint> /// for std::uint64_t
#include <iostream> /// for IO Operations
#include <numeric> /// for std::accumulate
#include <vector> /// for std::vector
/******************************************************************************
* @namespace dp
* @brief Dynamic programming algorithms
*******************************************************************************/
namespace dp {
/******************************************************************************
* @namespace partitionProblem
* @brief Partition problem algorithm
*******************************************************************************/
namespace partitionProblem {
/******************************************************************************
* @brief Returns true if arr can be partitioned in two subsets of equal sum,
* otherwise false
* @param arr vector containing elements
* @param size Size of the vector.
* @returns @param bool whether the vector can be partitioned or not.
*******************************************************************************/
bool findPartiion(const std::vector<uint64_t> &arr, uint64_t size) {
uint64_t sum = std::accumulate(arr.begin(), arr.end(),
0); // Calculate sum of all elements
if (sum % 2 != 0) {
return false; // if sum is odd, it cannot be divided into two equal sum
}
std::vector<bool> part;
// bool part[sum / 2 + 1];
// Initialize the part array as 0
for (uint64_t it = 0; it <= sum / 2; ++it) {
part.push_back(false);
}
// Fill the partition table in bottom up manner
for (uint64_t it = 0; it < size; ++it) {
// The element to be included in the sum cannot be greater than the sum
for (uint64_t it2 = sum / 2; it2 >= arr[it];
--it2) { // Check if sum - arr[i]
// ould be formed from a subset using elements before index i
if (part[it2 - arr[it]] == 1 || it2 == arr[it]) {
part[it2] = true;
}
}
}
return part[sum / 2];
}
} // namespace partitionProblem
} // namespace dp
/*******************************************************************************
* @brief Self-test implementations
* @returns void
*******************************************************************************/
static void test() {
std::vector<uint64_t> arr = {{1, 3, 3, 2, 3, 2}};
uint64_t n = arr.size();
bool expected_result = true;
bool derived_result = dp::partitionProblem::findPartiion(arr, n);
std::cout << "1st test: ";
assert(expected_result == derived_result);
std::cout << "Passed!" << std::endl;
}
/*******************************************************************************
* @brief Main function
* @returns 0 on exit
*******************************************************************************/
int main() {
test(); // run self-test implementations
return 0;
}
@@ -0,0 +1,80 @@
/*
*this program is use to find any elemet in any row with variable array size
*aplication of pointer is use in it
*important point start from here to:
*the index value of array can be go to 1 to 100000
*check till array[1000]
*end here
*how to work example:
**Question:
***number of array 2
***quarry 3
***array 1 is {1 2 3 4 5}
***array 2 is {6 7}
****i) what is 2nd element in 1st array
****ii) what is 1st element in 2nd array
****iii) what is 5th element in 1st array
*****output:
*****Enter Number of array you want to Store : 2
*****Enter Number of Question or Quary you want to do Related to Array : 3
*****Enter number of element in 1 rows : 5
*****Enter the element of Array 1 2 3 4 5
*****Enter number of element in 2 rows : 2
*****Enter the element of Array 6 7
*****enter the number of row which element You want to find : 1
*****enter the position of element which You want to find : 2
*****The element is 2
*****enter the number of row which element You want to find : 2
*****enter the position of element which You want to find : 1
*****The element is 6
*****enter the number of row which element You want to find : 1
*****enter the position of element which You want to find : 5
*****The element is 5
*/
#include <iostream>
// this is main fuction
// ***
int main() {
int64_t r, mr = 0, x, q, i, z;
std::cout << "Enter Number of array you want to Store :";
std::cin >> x;
std::cout << "Enter Number of ";
std::cout << "Question or Quary you ";
std::cout << "want to do Related to Array :";
std::cin >> q;
// create a Array in run time because use can
// change the size of each array which he/she is going to store
// create a 2D array
int** ar = new int*[x]();
// this for loop is use for entering different variable size array
// ***
for (r = 0; r < x; r++) {
std::cout << "Enter number of element in " << r + 1 << " rows :";
std::cin >> mr;
// creating a 1D array
int* ac = new int[mr]();
std::cout << "Enter the element of Array ";
// this for loop is use for storing values in array
// ***
for (i = 0; i < mr; i++) {
// entering the value of rows in array in Horizontal
std::cin >> ac[i];
}
// Change the position of Array so that new arrays entery will be done
ar[r] = ac;
}
// this for loop is use for display result of querry
// ***
for (z = 0; z < q; z++) {
int64_t r1 = 0, q1 = 0;
std::cout << "enter the number of row which element you want to find :";
std::cin >> r1;
r1 = r1 - 1;
std::cout << "enter the position of element which you want to find :";
std::cin >> q1;
q1 = q1 - 1;
// use this to find desire position of element in desire array
std::cout << "The element is " << ar[r1][q1] << std::endl;
}
}
@@ -0,0 +1,179 @@
/**
* @file
* @brief SCS is a string Z which is the shortest supersequence of strings X and Y (may not be continuous in Z, but order is maintained).
*
* @details
* The idea is to use lookup table method as used in LCS.
* For example: example 1:-
* X: 'ABCXYZ', Y: 'ABZ' then Z will be 'ABCXYZ' (y is not continuous but in order)
*
* For example: example 2:-
* X: 'AGGTAB', Y: 'GXTXAYB' then Z will be 'AGGXTXAYB'
* @author [Ridhish Jain](https://github.com/ridhishjain)
* @see more on [SCS](https://en.wikipedia.org/wiki/Shortest_common_supersequence_problem)
* @see related problem [Leetcode](https://leetcode.com/problems/shortest-common-supersequence/)
*/
// header files
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cassert>
/**
* @namespace dynamic_programming
* @brief Dynamic Programming algorithms
*/
namespace dynamic_programming {
/**
* @namespace shortest_common_supersequence
* @brief Shortest Common Super Sequence algorithm
*/
namespace shortest_common_supersequence {
/**
* Function implementing Shortest Common Super-Sequence algorithm using look-up table method.
* @param str1 first string 'X'
* @param str2 second string 'Y'
* @returns string 'Z', superSequence of X and Y
*/
std::string scs(const std::string &str1, const std::string &str2) {
// Edge cases
// If either str1 or str2 or both are empty
if(str1.empty() && str2.empty()) {
return "";
}
else if(str1.empty()) {
return str2;
}
else if(str2.empty()) {
return str1;
}
// creating lookup table
std::vector <std::vector <int>> lookup(str1.length() + 1, std::vector <int> (str2.length() + 1, 0));
for(int i=1; i <= str1.length(); i++) {
for(int j=1; j <= str2.length(); j++) {
if(str1[i-1] == str2[j-1]) {
lookup[i][j] = lookup[i-1][j-1] + 1;
}
else {
lookup[i][j] = std::max(lookup[i-1][j], lookup[i][j-1]);
}
}
}
// making supersequence
// i and j are initially pointed towards end of strings
// Super-sequence will be constructed backwards
int i=str1.length();
int j=str2.length();
std::string s;
while(i>0 && j>0) {
// If the characters at i and j of both strings are same
// We only need to add them once in s
if(str1[i-1] == str2[j-1]) {
s.push_back(str1[i-1]);
i--;
j--;
}
// otherwise we check lookup table for recurrences of characters
else {
if(lookup[i-1][j] > lookup[i][j-1]) {
s.push_back(str1[i-1]);
i--;
}
else {
s.push_back(str2[j-1]);
j--;
}
}
}
// copying remaining elements
// if j becomes 0 before i
while(i > 0) {
s.push_back(str1[i-1]);
i--;
}
// if i becomes 0 before j
while(j > 0) {
s.push_back(str2[j-1]);
j--;
}
// As the super sequence is constructd backwards
// reversing the string before returning gives us the correct output
reverse(s.begin(), s.end());
return s;
}
} // namespace shortest_common_supersequence
} // namespace dynamic_programming
/**
* Test Function
* @return void
*/
static void test() {
// custom input vector
std::vector <std::vector <std::string>> scsStrings {
{"ABCXYZ", "ABZ"},
{"ABZ", "ABCXYZ"},
{"AGGTAB", "GXTXAYB"},
{"X", "Y"},
};
// calculated output vector by scs function
std::vector <std::string> calculatedOutput(4, "");
int i=0;
for(auto & scsString : scsStrings) {
calculatedOutput[i] = dynamic_programming::shortest_common_supersequence::scs(
scsString[0], scsString[1]
);
i++;
}
// expected output vector acc to problem statement
std::vector <std::string> expectedOutput {
"ABCXYZ",
"ABCXYZ",
"AGGXTXAYB",
"XY"
};
// Testing implementation via assert function
// It will throw error if any of the expected test fails
// Else it will give nothing
for(int i=0; i < scsStrings.size(); i++) {
assert(expectedOutput[i] == calculatedOutput[i]);
}
std::cout << "All tests passed successfully!\n";
return;
}
/** Main function (driver code)*/
int main() {
// test for implementation
test();
// user input
std::string s1, s2;
std::cin >> s1;
std::cin >> s2;
std::string ans;
// user output
ans = dynamic_programming::shortest_common_supersequence::scs(s1, s2);
std::cout << ans;
return 0;
}
+123
View File
@@ -0,0 +1,123 @@
/**
* @file
* @brief Implements [Sub-set sum problem]
* (https://en.wikipedia.org/wiki/Subset_sum_problem) algorithm, which tells
* whether a subset with target sum exists or not.
*
* @details
* In this problem, we use dynamic programming to find if we can pull out a
* subset from an array whose sum is equal to a given target sum. The overall
* time complexity of the problem is O(n * targetSum) where n is the size of
* the array. For example, array = [1, -10, 2, 31, -6], targetSum = -14.
* Output: true => We can pick subset [-10, 2, -6] with sum as
* (-10) + 2 + (-6) = -14.
* @author [KillerAV](https://github.com/KillerAV)
*/
#include <cassert> /// for std::assert
#include <iostream> /// for IO operations
#include <unordered_map> /// for unordered map
#include <vector> /// for std::vector
/**
* @namespace dynamic_programming
* @brief Dynamic Programming algorithms
*/
namespace dynamic_programming {
/**
* @namespace subset_sum
* @brief Functions for [Sub-set sum problem]
* (https://en.wikipedia.org/wiki/Subset_sum_problem) algorithm
*/
namespace subset_sum {
/**
* Recursive function using dynamic programming to find if the required sum
* subset exists or not.
* @param arr input array
* @param targetSum the target sum of the subset
* @param dp the map storing the results
* @returns true/false based on if the target sum subset exists or not.
*/
bool subset_sum_recursion(const std::vector<int> &arr, int targetSum,
std::vector<std::unordered_map<int, bool>> *dp,
int index = 0) {
if (targetSum == 0) { // Found a valid subset with required sum.
return true;
}
if (index == arr.size()) { // End of array
return false;
}
if ((*dp)[index].count(targetSum)) { // Answer already present in map
return (*dp)[index][targetSum];
}
bool ans =
subset_sum_recursion(arr, targetSum - arr[index], dp, index + 1) ||
subset_sum_recursion(arr, targetSum, dp, index + 1);
(*dp)[index][targetSum] = ans; // Save ans in dp map.
return ans;
}
/**
* Function implementing subset sum algorithm using top-down approach
* @param arr input array
* @param targetSum the target sum of the subset
* @returns true/false based on if the target sum subset exists or not.
*/
bool subset_sum_problem(const std::vector<int> &arr, const int targetSum) {
size_t n = arr.size();
std::vector<std::unordered_map<int, bool>> dp(n);
return subset_sum_recursion(arr, targetSum, &dp);
}
} // namespace subset_sum
} // namespace dynamic_programming
/**
* @brief Test Function
* @return void
*/
static void test() {
// custom input vector
std::vector<std::vector<int>> custom_input_arr(3);
custom_input_arr[0] = std::vector<int>{1, -10, 2, 31, -6};
custom_input_arr[1] = std::vector<int>{2, 3, 4};
custom_input_arr[2] = std::vector<int>{0, 1, 0, 1, 0};
std::vector<int> custom_input_target_sum(3);
custom_input_target_sum[0] = -14;
custom_input_target_sum[1] = 10;
custom_input_target_sum[2] = 2;
// calculated output vector by pal_part Function
std::vector<int> calculated_output(3);
for (int i = 0; i < 3; i++) {
calculated_output[i] =
dynamic_programming::subset_sum::subset_sum_problem(
custom_input_arr[i], custom_input_target_sum[i]);
}
// expected output vector
std::vector<bool> expected_output{true, false, true};
// Testing implementation via assert function
// It will throw error if any of the expected test fails
// Else it will give nothing
for (int i = 0; i < 3; i++) {
assert(expected_output[i] == calculated_output[i]);
}
std::cout << "All tests passed successfully!\n";
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
test(); // execute the test
return 0;
}
+104
View File
@@ -0,0 +1,104 @@
/**
* @file
* @brief Implementation of the [Trapped Rainwater
* Problem](https://www.geeksforgeeks.org/trapping-rain-water/)
* @details
* This implementation calculates the amount of rainwater that can be trapped
* between walls represented by an array of heights.
* @author [SOZEL](https://github.com/TruongNhanNguyen)
*/
#include <algorithm> /// For std::min and std::max
#include <cassert> /// For assert
#include <cstddef> /// For std::size_t
#include <cstdint>
#include <vector> /// For std::vector
/*
* @namespace
* @brief Dynamic Programming Algorithms
*/
namespace dynamic_programming {
/**
* @brief Function to calculate the trapped rainwater
* @param heights Array representing the heights of walls
* @return The amount of trapped rainwater
*/
uint32_t trappedRainwater(const std::vector<uint32_t>& heights) {
std::size_t n = heights.size();
if (n <= 2)
return 0; // No water can be trapped with less than 3 walls
std::vector<uint32_t> leftMax(n), rightMax(n);
// Calculate the maximum height of wall to the left of each wall
leftMax[0] = heights[0];
for (std::size_t i = 1; i < n; ++i) {
leftMax[i] = std::max(leftMax[i - 1], heights[i]);
}
// Calculate the maximum height of wall to the right of each wall
rightMax[n - 1] = heights[n - 1];
for (std::size_t i = n - 2; i < n; --i) {
rightMax[i] = std::max(rightMax[i + 1], heights[i]);
}
// Calculate the trapped rainwater between walls
uint32_t trappedWater = 0;
for (std::size_t i = 0; i < n; ++i) {
trappedWater +=
std::max(0u, std::min(leftMax[i], rightMax[i]) - heights[i]);
}
return trappedWater;
}
} // namespace dynamic_programming
/**
* @brief Self-test implementations
* @returns void
*/
static void test() {
std::vector<uint32_t> test_basic = {0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1};
assert(dynamic_programming::trappedRainwater(test_basic) == 6);
std::vector<uint32_t> test_peak_under_water = {3, 0, 2, 0, 4};
assert(dynamic_programming::trappedRainwater(test_peak_under_water) == 7);
std::vector<uint32_t> test_bucket = {5, 1, 5};
assert(dynamic_programming::trappedRainwater(test_bucket) == 4);
std::vector<uint32_t> test_skewed_bucket = {4, 1, 5};
assert(dynamic_programming::trappedRainwater(test_skewed_bucket) == 3);
std::vector<uint32_t> test_empty = {};
assert(dynamic_programming::trappedRainwater(test_empty) == 0);
std::vector<uint32_t> test_flat = {0, 0, 0, 0, 0};
assert(dynamic_programming::trappedRainwater(test_flat) == 0);
std::vector<uint32_t> test_no_trapped_water = {1, 1, 2, 4, 0, 0, 0};
assert(dynamic_programming::trappedRainwater(test_no_trapped_water) == 0);
std::vector<uint32_t> test_single_elevation = {5};
assert(dynamic_programming::trappedRainwater(test_single_elevation) == 0);
std::vector<uint32_t> test_two_point_elevation = {5, 1};
assert(dynamic_programming::trappedRainwater(test_two_point_elevation) ==
0);
std::vector<uint32_t> test_large_elevation_map_difference = {5, 1, 6, 1,
7, 1, 8};
assert(dynamic_programming::trappedRainwater(
test_large_elevation_map_difference) == 15);
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
test(); // run self-test implementations
return 0;
}
+111
View File
@@ -0,0 +1,111 @@
/**
* @file
* @brief Implementation of the [Trapped Rainwater
* Problem](https://www.geeksforgeeks.org/trapping-rain-water/)
* @details
* This implementation calculates the total trapped rainwater using a
* two-pointer approach. It maintains two pointers (`left` and `right`) and
* tracks the maximum height seen so far from both ends (`leftMax` and
* `rightMax`). At each step, the algorithm decides which side to process based
* on which boundary is smaller, ensuring O(n) time and O(1) space complexity.
* @author [kanavgoyal898](https://github.com/kanavgoyal898)
*/
#include <algorithm> /// For std::min and std::max
#include <cassert> /// For assert
#include <cstddef> /// For std::size_t
#include <cstdint> /// For std::uint32_t
#include <vector> /// For std::vector
/*
* @namespace
* @brief Dynamic Programming Algorithms
*/
namespace dynamic_programming {
/**
* @brief Function to calculate the trapped rainwater
* @param heights Array representing the heights of walls
* @return The amount of trapped rainwater
*/
uint32_t trappedRainwater(const std::vector<uint32_t>& heights) {
std::size_t n = heights.size();
if (n <= 2)
return 0; // Not enough walls to trap water
std::size_t left = 0, right = n - 1;
uint32_t leftMax = 0, rightMax = 0, trappedWater = 0;
// Traverse from both ends towards the center
while (left < right) {
if (heights[left] < heights[right]) {
// Water trapped depends on the tallest wall to the left
if (heights[left] >= leftMax)
leftMax = heights[left]; // Update left max
else
trappedWater +=
leftMax - heights[left]; // Water trapped at current left
++left;
} else {
// Water trapped depends on the tallest wall to the right
if (heights[right] >= rightMax)
rightMax = heights[right]; // Update right max
else
trappedWater +=
rightMax -
heights[right]; // Water trapped at current right
--right;
}
}
return trappedWater;
}
} // namespace dynamic_programming
/**
* @brief Self-test implementations
* @returns void
*/
static void test() {
std::vector<uint32_t> test_basic = {0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1};
assert(dynamic_programming::trappedRainwater(test_basic) == 6);
std::vector<uint32_t> test_peak_under_water = {3, 0, 2, 0, 4};
assert(dynamic_programming::trappedRainwater(test_peak_under_water) == 7);
std::vector<uint32_t> test_bucket = {5, 1, 5};
assert(dynamic_programming::trappedRainwater(test_bucket) == 4);
std::vector<uint32_t> test_skewed_bucket = {4, 1, 5};
assert(dynamic_programming::trappedRainwater(test_skewed_bucket) == 3);
std::vector<uint32_t> test_empty = {};
assert(dynamic_programming::trappedRainwater(test_empty) == 0);
std::vector<uint32_t> test_flat = {0, 0, 0, 0, 0};
assert(dynamic_programming::trappedRainwater(test_flat) == 0);
std::vector<uint32_t> test_no_trapped_water = {1, 1, 2, 4, 0, 0, 0};
assert(dynamic_programming::trappedRainwater(test_no_trapped_water) == 0);
std::vector<uint32_t> test_single_elevation = {5};
assert(dynamic_programming::trappedRainwater(test_single_elevation) == 0);
std::vector<uint32_t> test_two_point_elevation = {5, 1};
assert(dynamic_programming::trappedRainwater(test_two_point_elevation) ==
0);
std::vector<uint32_t> test_large_elevation_map_difference = {5, 1, 6, 1,
7, 1, 8};
assert(dynamic_programming::trappedRainwater(
test_large_elevation_map_difference) == 15);
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
test(); // run self-test implementations
return 0;
}
+70
View File
@@ -0,0 +1,70 @@
// C++ Program to find height of the tree using bottom-up dynamic programming.
/*
* Given a rooted tree with node 1.
* Task is to find the height of the tree.
* Example: -
* 4
* 1 2
* 1 3
* 2 4
* which can be represented as
* 1
* / \
* 2 3
* |
* 4
*
* Height of the tree : - 2
*/
#include <iostream>
#include <vector>
// global declarations
// no of nodes max limit.
const int MAX = 1e5;
// adjacency list
std::vector<int> adj[MAX];
std::vector<bool> visited;
std::vector<int> dp;
void depth_first_search(int u) {
visited[u] = true;
int child_height = 1;
for (int v : adj[u]) {
if (!visited[v]) {
depth_first_search(v);
// select maximum sub-tree height from all children.
child_height = std::max(child_height, dp[v] + 1);
}
}
// assigned the max child height to current visited node.
dp[u] = child_height;
}
int main() {
// number of nodes
int number_of_nodes;
std::cout << "Enter number of nodes of the tree : " << std::endl;
std::cin >> number_of_nodes;
// u, v denotes an undirected edge of tree.
int u, v;
// Tree contains exactly n-1 edges where n denotes the number of nodes.
std::cout << "Enter edges of the tree : " << std::endl;
for (int i = 0; i < number_of_nodes - 1; i++) {
std::cin >> u >> v;
// undirected tree u -> v and v -> u.
adj[u].push_back(v);
adj[v].push_back(u);
}
// initialize all nodes as unvisited.
visited.assign(number_of_nodes + 1, false);
// initialize depth of all nodes to 0.
dp.assign(number_of_nodes + 1, 0);
// function call which will initialize the height of all nodes.
depth_first_search(1);
std::cout << "Height of the Tree : " << dp[1] << std::endl;
}
@@ -0,0 +1,173 @@
/**
* @file
* @brief Implementation of the Unbounded 0/1 Knapsack Problem
*
* @details
* The Unbounded 0/1 Knapsack problem allows taking unlimited quantities of each
* item. The goal is to maximize the total value without exceeding the given
* knapsack capacity. Unlike the 0/1 knapsack, where each item can be taken only
* once, in this variation, any item can be picked any number of times as long
* as the total weight stays within the knapsack's capacity.
*
* Given a set of N items, each with a weight and a value, represented by the
* arrays `wt` and `val` respectively, and a knapsack with a weight limit W, the
* task is to fill the knapsack to maximize the total value.
*
* @note weight and value of items is greater than zero
*
* ### Algorithm
* The approach uses dynamic programming to build a solution iteratively.
* A 2D array is used for memoization to store intermediate results, allowing
* the function to avoid redundant calculations.
*
* @author [Sanskruti Yeole](https://github.com/yeolesanskruti)
* @see dynamic_programming/0_1_knapsack.cpp
*/
#include <cassert> // For using assert function to validate test cases
#include <cstdint> // For fixed-width integer types like std::uint16_t
#include <iostream> // Standard input-output stream
#include <vector> // Standard library for using dynamic arrays (vectors)
/**
* @namespace dynamic_programming
* @brief Namespace for dynamic programming algorithms
*/
namespace dynamic_programming {
/**
* @namespace Knapsack
* @brief Implementation of unbounded 0-1 knapsack problem
*/
namespace unbounded_knapsack {
/**
* @brief Recursive function to calculate the maximum value obtainable using
* an unbounded knapsack approach.
*
* @param i Current index in the value and weight vectors.
* @param W Remaining capacity of the knapsack.
* @param val Vector of values corresponding to the items.
* @note "val" data type can be changed according to the size of the input.
* @param wt Vector of weights corresponding to the items.
* @note "wt" data type can be changed according to the size of the input.
* @param dp 2D vector for memoization to avoid redundant calculations.
* @return The maximum value that can be obtained for the given index and
* capacity.
*/
std::uint16_t KnapSackFilling(std::uint16_t i, std::uint16_t W,
const std::vector<std::uint16_t>& val,
const std::vector<std::uint16_t>& wt,
std::vector<std::vector<int>>& dp) {
if (i == 0) {
if (wt[0] <= W) {
return (W / wt[0]) *
val[0]; // Take as many of the first item as possible
} else {
return 0; // Can't take the first item
}
}
if (dp[i][W] != -1)
return dp[i][W]; // Return result if available
int nottake =
KnapSackFilling(i - 1, W, val, wt, dp); // Value without taking item i
int take = 0;
if (W >= wt[i]) {
take = val[i] + KnapSackFilling(i, W - wt[i], val, wt,
dp); // Value taking item i
}
return dp[i][W] =
std::max(take, nottake); // Store and return the maximum value
}
/**
* @brief Wrapper function to initiate the unbounded knapsack calculation.
*
* @param N Number of items.
* @param W Maximum weight capacity of the knapsack.
* @param val Vector of values corresponding to the items.
* @param wt Vector of weights corresponding to the items.
* @return The maximum value that can be obtained for the given capacity.
*/
std::uint16_t unboundedKnapsack(std::uint16_t N, std::uint16_t W,
const std::vector<std::uint16_t>& val,
const std::vector<std::uint16_t>& wt) {
if (N == 0)
return 0; // Expect 0 since no items
std::vector<std::vector<int>> dp(
N, std::vector<int>(W + 1, -1)); // Initialize memoization table
return KnapSackFilling(N - 1, W, val, wt, dp); // Start the calculation
}
} // namespace unbounded_knapsack
} // namespace dynamic_programming
/**
* @brief self test implementation
* @return void
*/
static void tests() {
// Test Case 1
std::uint16_t N1 = 4; // Number of items
std::vector<std::uint16_t> wt1 = {1, 3, 4, 5}; // Weights of the items
std::vector<std::uint16_t> val1 = {6, 1, 7, 7}; // Values of the items
std::uint16_t W1 = 8; // Maximum capacity of the knapsack
// Test the function and assert the expected output
assert(dynamic_programming::unbounded_knapsack::unboundedKnapsack(
N1, W1, val1, wt1) == 48);
std::cout << "Maximum Knapsack value "
<< dynamic_programming::unbounded_knapsack::unboundedKnapsack(
N1, W1, val1, wt1)
<< std::endl;
// Test Case 2
std::uint16_t N2 = 3; // Number of items
std::vector<std::uint16_t> wt2 = {10, 20, 30}; // Weights of the items
std::vector<std::uint16_t> val2 = {60, 100, 120}; // Values of the items
std::uint16_t W2 = 5; // Maximum capacity of the knapsack
// Test the function and assert the expected output
assert(dynamic_programming::unbounded_knapsack::unboundedKnapsack(
N2, W2, val2, wt2) == 0);
std::cout << "Maximum Knapsack value "
<< dynamic_programming::unbounded_knapsack::unboundedKnapsack(
N2, W2, val2, wt2)
<< std::endl;
// Test Case 3
std::uint16_t N3 = 3; // Number of items
std::vector<std::uint16_t> wt3 = {2, 4, 6}; // Weights of the items
std::vector<std::uint16_t> val3 = {5, 11, 13}; // Values of the items
std::uint16_t W3 = 27; // Maximum capacity of the knapsack
// Test the function and assert the expected output
assert(dynamic_programming::unbounded_knapsack::unboundedKnapsack(
N3, W3, val3, wt3) == 27);
std::cout << "Maximum Knapsack value "
<< dynamic_programming::unbounded_knapsack::unboundedKnapsack(
N3, W3, val3, wt3)
<< std::endl;
// Test Case 4
std::uint16_t N4 = 0; // Number of items
std::vector<std::uint16_t> wt4 = {}; // Weights of the items
std::vector<std::uint16_t> val4 = {}; // Values of the items
std::uint16_t W4 = 10; // Maximum capacity of the knapsack
assert(dynamic_programming::unbounded_knapsack::unboundedKnapsack(
N4, W4, val4, wt4) == 0);
std::cout << "Maximum Knapsack value for empty arrays: "
<< dynamic_programming::unbounded_knapsack::unboundedKnapsack(
N4, W4, val4, wt4)
<< std::endl;
std::cout << "All test cases passed!" << std::endl;
}
/**
* @brief main function
* @return 0 on successful exit
*/
int main() {
tests(); // Run self test implementation
return 0;
}
+186
View File
@@ -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 <cassert>
#include <climits>
#include <iostream>
#include <string>
#include <unordered_set>
#include <vector>
/**
* @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<std::string> &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<std::string> &strSet,
int pos, std::vector<int> *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) && 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<std::string> &wordDict) {
// unordered set to store words in the dictionary for constant time
// search
std::unordered_set<std::string> 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<int> 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<std::string> 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<std::string> 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;
}