# DSA 4 - Interview Problems
---
### Problem 1 Target Sum
You are given a set of non-negative integers and a target sum. The task is to determine whether there exists a subset of the given set whose sum is equal to the target sum.
**Examples**
**Input**: set = {3, 34, 4, 12, 5, 2}, sum = 9
**Output**: True
**Explanation**: There is a subset (4, 5) with a sum of 9.
:::warning
Please take some time to think about the brute force approach on your own before reading further.....
:::
#### Approach: Brute Force
The brute force approach involves exploring all possible subsets of the given set. This is achieved through a recursive function named isSubsetSum(set, n, sum). The function explores two possibilities for each element: including it in the subset or excluding it. The base cases check if the sum is zero or if there are no more elements to consider.
#### Code
```java
boolean isSubsetSum(int[] set, int n, int sum) {
if (sum == 0) return true;
if (n == 0 && sum != 0) return false;
// Explore two possibilities: include the current element or exclude it
return isSubsetSum(set, n - 1, sum) || isSubsetSum(set, n - 1, sum - set[n - 1]);
}
```
#### Complexity Analysis
* **Time complexity**: O(2^n) - Exponential
* **Space complexity**: O(1)
---
### Target Sum Dynamic Programming Approach
#### Approach:
To optimize the solution, dynamic programming is employed. A 2D array `dp` is used to store results of subproblems. The value `dp[i][j]` represents whether it's possible to obtain a sum of `j` using the first `i` elements of the set.
#### Code
```java
boolean isSubsetSum(int[] set, int n, int sum) {
boolean[][] dp = new boolean[n + 1][sum + 1];
// Initialize the first column as true, as sum 0 is always possible
for (int i = 0; i <= n; i++) dp[i][0] = true;
// Fill the dp array using a bottom-up approach
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= sum; j++) {
dp[i][j] = dp[i - 1][j];
if (j >= set[i - 1]) dp[i][j] = dp[i][j] || dp[i - 1][j - set[i - 1]];
}
}
return dp[n][sum];
}
```
#### Complexity Analysis
* **Time complexity**: O(n * sum)
* **Space complexity**: O(n * sum)
---
### Problem 2 Minimum Jumps to Reach End
You are given a 0-indexed array of integers nums of length n. You are initially positioned at nums[0].
Each element nums[i] represents the maximum length of a forward jump from index i. In other words, if you are at nums[i], you can jump to any nums[i + j] where:
* 0 <= j <= nums[i]
* i + j < n
Return the minimum number of jumps to reach nums[n - 1]. The test cases are generated such that you can reach nums[n - 1].
#### Example 1
**Input**: nums = [2,3,1,1,4]
**Output**: 2
**Explanation**: The minimum number of jumps to reach the last index is 2. Jump 1 step from index 0 to 1, then 3 steps to the last index.
#### Example 2
**Input**: nums = [2,3,0,1,4]
**Output**: 2
:::warning
Please take some time to think about the solution approach on your own before reading further.....
:::
#### Intuition
The code utilizes a greedy approach, aiming to minimize the number of jumps by selecting the jump that maximizes the range of reachable positions at each step.
#### Approach
* Initialize a counter to keep track of the number of jumps (counter).
* Use a while loop to iterate through the array until the end is reached.
* In each iteration:
* Calculate the range of positions that can be reached from the current position (range).
* If the calculated range includes the last position or exceeds it, exit the loop.
* Find the position within the current range that maximizes the next reachable position (temp).
* Update the current position to the selected position (temp).
* Increment the jump counter (counter).
* Repeat the process until the end is reached.
#### Code
```java
class Solution {
public int jump(int[] nums) {
int i = 0;
int counter = 0;
// If the array has only one element, no jumps are needed
if (nums.length == 1)
return 0;
while (i < nums.length) {
counter++;
int range = i + nums[i];
// If the range includes the last position or exceeds it, exit the loop
if (range >= nums.length - 1)
break;
int max = 0;
int temp = 0;
// Find the position within the current range that maximizes the next reachable position
for (int k = i + 1; k <= range; k++) {
if (nums[k] + k >= max) {
max = nums[k] + k;
temp = k;
}
}
i = temp;
}
return counter;
}
}
```
#### Complexity Analysis
**Time complexity**: O(n)
* The algorithm iterates through each element of the array once.
* The inner loop within each iteration also traverses a limited number of positions.
**Space complexity**: O(1)
* The algorithm uses a constant amount of extra space.
* The space requirements do not depend on the size of the input array.
---
### Problem 3 N digit numbers
Find out the number of A digit positive numbers, whose digits on being added equals to a given number B.
Note that a valid number starts from digits 1-9 except the number 0 itself. i.e. leading zeroes are not allowed.
Since the answer can be large, output answer modulo 1000000007
:::warning
Please take some time to think about the brute force approach on your own before reading further.....
:::
### Brute Force Approach
#### Objective
Count the number of A-digit positive numbers whose digit sum equals a given number B.
#### Idea
* Generate all possible A-digit numbers
* For each number, check if the sum of its digits equals B.
* Keep track of the count of valid numbers.
#### Code
```cpp
int bruteForceCount(int A, int B) {
int count = 0;
for (int num = pow(10, A - 1); num < pow(10, A); ++num) {
int digitSum = 0;
int temp = num;
while (temp > 0) {
digitSum += temp % 10;
temp /= 10;
}
if (digitSum == B) {
count++;
}
}
return count;
}
```
---
### N digit numbers optimization using Recursion
#### Observation
The brute force approach involves iterating through all A digit numbers and checking the count of numbers whose digit sum equals B.
#### Optimized Recursive Approach
**Recursive Function Definition**
* The recursive function `countWays(id, sum)` is defined to represent the count of A-digit numbers with a digit sum equal to sum.
* The base cases are as follows:
* If sum becomes negative, it implies that the digit sum has exceeded the target, so the count is 0.
* If id becomes 0, meaning all digits have been considered, the count is 1 if sum is 0 (indicating the target sum has been achieved), and 0 otherwise.
* Memoization is used to store and retrieve previously calculated values, preventing redundant computations.
**Memoization Table**
* The memo vector is a 2D table of size (A + 1) x (B + 1) initialized with -1 to represent uncalculated states.
* The value `memo[id][sum]` stores the count of A-digit numbers with a digit sum of sum that has already been calculated.
**Recursive Part**
* The function explores all possible digits from 0 to 9 in a loop.
* For each digit, it recursively calls `countWays(id - 1, sum - digit, memo)` to calculate the count of (A-1)-digit numbers with the updated digit sum.
* The results are summed up, and the modulus operation is applied to avoid integer overflow.
#### Optimized Recursive Code
```cpp
int memoizationCount(int A, int B) {
vector<vector<int>> memo(A + 1, vector<int>(B + 1, -1));
return countWays(A, B, memo);
}
int countWays(int id, int sum, vector<vector<int>>& memo) {
if (sum < 0) return 0;
if (id == 0) return (sum == 0) ? 1 : 0;
if (memo[id][sum] != -1) return memo[id][sum];
int ways = 0;
for (int digit = 0; digit <= 9; ++digit) {
ways += countWays(id - 1, sum - digit, memo);
ways %= 1000000007;
}
return memo[id][sum] = ways;
}
```
#### Optimized Iterative Code
```cpp
int iterativeCount(int A, int B) {
vector<vector<int>> dp(A + 1, vector<int>(B + 1, 0));
// Base case
for (int digit = 1; digit <= 9; ++digit) {
if (digit <= B) dp[1][digit] = 1;
}
// Build DP table
for (int id = 2; id <= A; ++id) {
for (int sum = 1; sum <= B; ++sum) {
for (int digit = 1; digit <= 9; ++digit) {
if (sum - digit >= 0) {
dp[id][sum] += dp[id - 1][sum - digit];
dp[id][sum] %= 1000000007;
}
}
}
}
return dp[A][B];
}
```
**Time Complexity** : O(A * B)
**Space Complexity** : O(A * B)
---
### Problem 4 Maximum Profit from Stock Prices
Given an array A where the i-th element represents the price of a stock on day i, the objective is to find the maximum profit. We're allowed to complete as many transactions as desired, but engaging in multiple transactions simultaneously is not allowed.
:::warning
Please take some time to think about the solution approach on your own before reading further.....
:::
#### Approach
Let's start with some key observations:
**Note 1**: It's never beneficial to buy a stock and sell it at a loss. This intuitive insight guides our decision-making process.
**Note 2**: If the stock price on day i is less than the price on day i+1, it's always advantageous to buy on day i and sell on day i+1.
Now, let's delve into the rationale behind Note 2:
**Proof**: If the price on day i+1 is higher than the price on day i, buying on day i and selling on day i+1 ensures a profit. If we didn't sell on day i+1 and waited for day i+2 to sell, the profit would still be the same. Thus, it's optimal to sell whenever there's a price increase.
#### DP-Based Solution
Now, let's transition to a dynamic programming solution based on the following recurrence relation:
Let Dp[i] represent the maximum profit you can gain in the region (i, i+1, ..., n).
**Recurrence Relation**: `Dp[i] = max(Dp[i+1], -A[i] + max(A[j] + Dp[j] for j > i))`
Here, Dp[i] considers either continuing with the profit from the next day (Dp[i+1]) or selling on day i and adding the profit from subsequent days.
#### Base Cases
When i is the last day (i == n-1), Dp[i] = 0 since there are no more future days.
When i is not the last day, Dp[i] needs to be computed using the recurrence relation.
#### Direction of Computation
We start computing Dp[i] from the last day and move towards the first day.
#### Code
```cpp
int max_profit(vector<int>& A) {
int n = A.size();
vector<int> dp(n, 0);
for (int i = n - 2; i >= 0; --i) {
dp[i] = dp[i + 1];
for (int j = i + 1; j < n; ++j) {
if (j + 1 < n) {
dp[i] = max(dp[i], -A[i] + A[j] + dp[j + 1]);
} else {
dp[i] = max(dp[i], -A[i] + A[j]);
}
}
}
return dp[0];
}
```
#### Optimized Code
The provided code snippet in C++ contains this observation-based solution. It iterates through the array, checks for price increases, and accumulates the profits accordingly.
```cpp
int Solution::maxProfit(const vector<int> &A) {
int total = 0, sz = A.size();
for (int i = 0; i < sz - 1; i++) {
if (A[i + 1] > A[i])
total += A[i + 1] - A[i];
}
return total;
}
```
**Time Complexity** : O(|A|)
**Space Complexity** : O(1)