Problem
There are n
soldiers standing in a line. Each soldier is assigned a unique rating
value.
You have to form a team of 3 soldiers amongst them under the following rules:
- Choose 3 soldiers with index (
i
,j
,k
) with rating (rating[i]
,rating[j]
,rating[k]
). - A team is valid if: (
rating[i] < rating[j] < rating[k]
) or (rating[i] > rating[j] > rating[k]
) where (0 <= i < j < k < n
).
Return the number of teams you can form given the conditions. (soldiers can be part of multiple teams).
Examples
Example 1:
Input: rating = [2,5,3,4,1]
Output: 3
Explanation: We can form three teams given the conditions. (2,3,4), (5,4,1), (5,3,1).
Example 2:
Input: rating = [2,1,3]
Output: 0
Explanation: We can't form any team given the conditions.
Example 3:
Input: rating = [1,2,3,4]
Output: 4
Solution
Here is the video explanation of below approaches:
Method 1 - Brute Force
We can use three nested loops to check all possible triplets. This approach is simple but not efficient for larger values of ( n ) (time complexity O(n^3)
).
Method 2 - Count Left and Right
- For each soldier at index ( j ), we can count the number of soldiers before ( j ) that are either less than or greater than the rating of ( j ).
- Similarly, count the number of soldiers after ( j ) that are either less than or greater than the rating of ( j ).
- Using these counts, we can determine the number of valid teams for which ( j ) is the middle soldier.
We will iterate through each soldier to collect these counts and then compute the number of valid teams.
Code
Java
class Solution {
public int numTeams(int[] rating) {
int n = rating.length;
int count = 0;
for (int j = 0; j < n; j++) {
int leftSmaller = 0;
int leftGreater = 0;
int rightSmaller = 0;
int rightGreater = 0;
// Count soldiers before j
for (int i = 0; i < j; i++) {
if (rating[i]<rating[j]) {
leftSmaller++;
} else if (rating[i] > rating[j]) {
leftGreater++;
}
}
// Count soldiers after j
for (int k = j + 1; k < n; k++) {
if (rating[k]<rating[j]) {
rightSmaller++;
} else if (rating[k] > rating[j]) {
rightGreater++;
}
}
// Valid teams with j as the middle soldier
count += (leftSmaller * rightGreater) + (leftGreater * rightSmaller);
}
return count;
}
}
Complexity
- ⏰ Time complexity:
O(n^2)
- 🧺 Space complexity:
O(1)
Method 3 - Recursion
- This problem is a variation of the Longest Increasing Subsequence LIS Problem.
- Instead of finding the LIS and Longest Decreasing Subsequence (LDS), we need to count the total number of increasing and decreasing subsequences of length 3 in the given array.
- While LIS determines the maximum length of increasing subsequences, our goal here is to specifically focus on subsequences of length 3.
Code
Java
class Solution {
public int numTeams(int[] rating) {
int ans1 = dfsAsc(rating, 0, -1, 3);
int ans2 = dfsDesc(rating, 0, -1, 3);
return ans1+ans2;
}
public int dfsAsc(int[] rating, int ind, int prev, int count){
if(count == 0){
return 1;
}
if(ind == rating.length){
return 0;
}
int notPick = dfsAsc(rating, ind+1, prev, count);
int pick = 0;
if(prev == -1 || rating[ind]>rating[prev]){
pick = dfsAsc(rating, ind+1, ind, count-1);
}
return pick + notPick;
}
public int dfsDesc(int[] rating, int ind, int prev, int count){
if(count == 0){
return 1;
}
if(ind == rating.length){
return 0;
}
int notPick = dfsDesc(rating, ind+1, prev, count);
int pick = 0;
if(prev == -1 || rating[ind]<rating[prev]){
pick = dfsDesc(rating, ind+1, ind, count-1);
}
return pick + notPick;
}
}
Complexity
- ⏰ Time complexity:
O(2^n)
- Each element can be either included or excluded, leading to (O(2^n)) combinations in total.
- For each combination, checking if it forms a valid team takes constant time.
- 🧺 Space complexity:
O(n)
for recursion stack
Method 4 - Top Down DP with memoization
Code
Java
class Solution {
private Integer[][][] dpAsc;
private Integer[][][] dpDesc;
public int numTeams(int[] rating) {
dpAsc = new Integer[rating.length][rating.length+1][4];
dpDesc = new Integer[rating.length][rating.length+1][4];
int ans1 = dfsAsc(rating, 0, -1, 3);
int ans2 = dfsDesc(rating, 0, -1, 3);
return ans1+ans2;
}
public int dfsAsc(int[] rating, int ind, int prev, int count){
if(count == 0){
return 1;
}
if(ind == rating.length){
return 0;
}
if(dpAsc[ind][prev+1][count]!=null){
return dpAsc[ind][prev+1][count];
}
int notPick = dfsAsc(rating, ind+1, prev, count);
int pick = 0;
if(prev == -1 || rating[ind]>rating[prev]){
pick = dfsAsc(rating, ind+1, ind, count-1);
}
return dpAsc[ind][prev+1][count] = pick + notPick;
}
public int dfsDesc(int[] rating, int ind, int prev, int count){
if(count == 0){
return 1;
}
if(ind == rating.length){
return 0;
}
if(dpDesc[ind][prev+1][count]!=null){
return dpDesc[ind][prev+1][count];
}
int notPick = dfsDesc(rating, ind+1, prev, count);
int pick = 0;
if(prev == -1 || rating[ind]<rating[prev]){
pick = dfsDesc(rating, ind+1, ind, count-1);
}
return dpDesc[ind][prev+1][count] = pick + notPick;
}
}
Complexity
- ⏰ Time complexity:
O(n^2)
- The function is called with different values of
(ind, prev, count)
. ind
ranges from0
ton
.prev
can be any of then
indices or-1
, resulting inn + 1
possible values.count
ranges from0
to3
.
- The function is called with different values of
- 🧺 Space complexity:
O(n^2)
due to memoization table and recursion stack (of size ofO(n)
)
Method 5 - Bottom up DP
Code
Java
class Solution {
public int numTeams(int [] rating) {
int n = rating.length;
int [] dp = new int[n];
int count = 0;
for (int i=0; i< n; i++) {
for (int j=i; j>=0; j--) {
if (rating[i] < rating[j]) {
dp[i] += 1;
count += dp[j];
}
}
}
dp = new int[n];
for (int i=0; i<n; i++) {
for (int j=i; j>=0; j--) {
if (rating[i] > rating[j]) {
dp[i] += 1;
count += dp[j];
}
}
}
return count;
}
}
Complexity
- ⏰ Time complexity:
O(n^2)
- 🧺 Space complexity:
O(n^2)