Need more Efficient way to read subarrays - arrays

The problem statement asks the number of such subarrays where i < j < k, such that sum of any two numbers should be greater than or equal to the third in the subarray:
What I did:
I ran a loop from i=0 till n-2:
and the basic logic I used was if the first two elements in the sorted subarray are greater than or equal to the maximum, then all pairs will be greater than any element. and every time I get the subarray, I add the next element into it and set those three variables again. Am passing 15/20 TCs other am getting TLE:
Constraints:
1<=n<=10^5
1<=ai<=10^9
for(int i=0;i<n-2;i++)
{
int r=i+2;
vector<int> temp(inp.begin()+i,inp.begin()+r+1);
sort(temp.begin(),temp.end());
max_elem=temp[1];min_elem=temp[0];
int maximum=temp[temp.size()-1];
//cout<<max_elem<<" "<<min_elem<<"\n";
while(r<n && max_elem+min_elem >= maximum)
{
//cout<<max_elem<<" "<<min_elem<<" "<<inp[r]<<"\n";
cnt++;
r++;
if(inp[r]<min_elem) {max_elem=min_elem;min_elem=inp[r];}
else if(inp[r]<max_elem) max_elem=inp[r];
else if(inp[r]>maximum) maximum=inp[r];
}
}
cout<<cnt<<"\n";
Sample TC:
I1:
5
7 6 5 3 4
O1:
6
Explanation:
6 subarrays fulfill the conditions: (7,6,5),(7,6,5,3),(7,6,5,3,4),(6,5,3),(6,5,3,4),(5,3,4).
I2:
5
1 2 3 5 6
O2:
3
Explanation:
(1,2,3),(2,3,5),(3,5,6) --(NOTE: 1,2,3,5 isn't the ans coz 1+2 < 5 )

A naive approach to do this is this is as the following. Your logic is correct and it is what I implemented. I changed the sort (NlogN) with a single pass (N) finding only the 2 smallest and largest numbers. I haven't compiled the code and not sure it works as intended. It has the overall complexity of (N*N*N).
Execution time can be improved by doing some extra checks:
min1 + min2 >= maxcondition can be checked after each inner (k) loop, breaking if it violates for single case.
If condition is not satisfied for say subarray 4-7, there is no need to check any other substring including 4-7. By storing violating cases and checking against them before each loop, overall execution time can be improved.
int min1;
int min2;
int max;
int count = 0;
for(int i = 2; i < n; i++){
for(int j = 0; j < i - 2; j++){
max = -1;
min1 = min2 = 1000000000;
for(int k = j; k <= i; k++){
if(inp[k] > max)
max = inp[k];
if(inp[k] < min1){
min1 = inp[k];
continue;
}
if(inp[k] < min2){
min2 = inp[k];
}
}
if(min1 + min2 >= max)
count++;
}
}

There might be some bugs, but here is the general idea for a O(n log n) solution:
We keep a windows of elements from startIdx to endIdx. If its a valid subarray, it means we can expand it, we can add another element to it, so we increase endIdx. If its not valid, it wouldnt be valid no matter how much we expand it, so we need to reduce it by increasing startIdx.
pseudocode:
multiset<int> nums;
int startIdx = 0, endIdx = 0;
int sol = 0;
while(endIdx != inp.size()) {
if (endIdx - startIdx < 3) {
nums.add(inp[endIdx]);
endIdx++;
} else {
if (nums.lowestElement() + nums.secondLowestElement() < nums.highestElement()) {
nums.remove(nums.find(inp[startIdx]));
startIdx++;
} else {
sol += endIdx - startIdx - 2; // amount of valid subarrays ending in inp[endIdx - 1]
nums.add(inp[endIdx]);
endIdx++;
}
}
}

Related

Non divisible subset-Hackerrank solution in C

I am new to programming and C is the only language I know. Read a few answers for the same question written in other programming languages. I have written some code for the same but I only get a few test cases correct (4 to be precise). How do I edit my code to get accepted?
I have tried comparing one element of the array with the rest and then I remove the element (which is being compared with the initial) if their sum is divisible by k and then this continues until there are two elements in the array where their sum is divisible by k. Here is the link to the question:
https://www.hackerrank.com/challenges/non-divisible-subset/problem
#include<stdio.h>
#include<stdlib.h>
void remove_element(int array[],int position,long int *n){
int i;
for(i=position;i<=(*n)-1;i++){
array[i]=array[i+1];
}
*n=*n-1;
}
int main(){
int k;
long int n;
scanf("%ld",&n);
scanf("%d",&k);
int *array=malloc(n*sizeof(int));
int i,j;
for(i=0;i<n;i++)
scanf("%d",&array[i]);
for(i=n-1;i>=0;i--){
int counter=0;
for(j=n-1;j>=0;j--){
if((i!=j)&&(array[i]+array[j])%k==0)
{
remove_element(array,j,&n);
j--;
continue;
}
else if((i!=j)&&(array[i]+array[j])%k!=0){
counter++;
}
}
if(counter==n-1){
printf("%ld",n);
break;
}
}
return 0;
}
I only get about 4 test cases right from 20 test cases.
What Gerhardh in his comment hinted at is that
for(i=position;i<=(*n)-1;i++){
array[i]=array[i+1];
}
reads from array[*n] when i = *n-1, overrunning the array. Change that to
for (i=position; i<*n-1; i++)
array[i]=array[i+1];
Additionally, you have
remove_element(array,j,&n);
j--;
- but j will be decremented when continuing the for loop, so decrementing it here is one time too many, while adjustment of i is necessary, since remove_element() shifted array[i] one position to the left, so change j-- to i--.
Furthermore, the condition
if(counter==n-1){
printf("%ld",n);
break;
}
makes just no sense; remove that block and place printf("%ld\n", n); before the return 0;.
To solve this efficiently, you have to realize several things:
Two positive integer numbers a and b are divisible by k (also positive integer number) if ((a%k) + (b%k))%k = 0. That means, that either ((a%k) + (b%k)) = 0 (1) or ((a%k) + (b%k)) = k (2).
Case (1) ((a%k) + (b%k)) = 0 is possible only if both a and b are multiples of k or a%k=0 and b%k=0. For case (2) , there are at most k/2 possible pairs. So, our task is to pick elements that don't fall in case 1 or 2.
To do this, map each number in your array to its corresponding remainder by modulo k. For this, create a new array remainders in which an index stands for a remainder, and a value stands for numbers having such remainder.
Go over the new array remainders and handle 3 cases.
4.1 If remainders[0] > 0, then we can still pick only one element from the original (if we pick more, then sum of their remainders 0, so they are divisible by k!!!).
4.2 if k is even and remainders[k/2] > 0, then we can also pick only one element (otherwise their sum is k!!!).
4.3 What about the other numbers? Well, for any remainder rem > 0 make sure to pick max(remainders[rem], remainders[k - rem]). You can't pick both since rem + k - rem = k, so numbers from such groups can be divisible by k.
Now, the code:
int nonDivisibleSubset(int k, int s_count, int* s) {
static int remainders[101];
for (int i = 0; i < s_count; i++) {
int rem = s[i] % k;
remainders[rem]++;
}
int maxSize = 0;
bool isKOdd = k & 1;
int halfK = k / 2;
for (int rem = 0; rem <= halfK; rem++) {
if (rem == 0) {
maxSize += remainders[rem] > 0;
continue;
}
if (!isKOdd && (rem == halfK)) {
maxSize++;
continue;
}
int otherRem = k - rem;
if (remainders[rem] > remainders[otherRem]) {
maxSize += remainders[rem];
} else {
maxSize += remainders[otherRem];
}
}
return maxSize;
}

Sort array of numbers (first number will be dynamically selected and remaining array should be sorted ascending)

int[] array = {1,1,0,1,2,2,0,0};
int firstNumber = 1;// dynamic can be 0 or 1 or 2
int numberOfOccurances = 0;
//Basic sort functionality
for(int i = 0 ; i< array.length; ++i)
{
if(array[i] == firstNumber)
{
numberOfOccurances++;
}
for(int j = i+1; j<array.length; ++j)
{
if(array[j] < array[i])
{
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
int[] requiredArray= new int[array.length];
for(int i = array.length-1 ; i >= 0; i--)
{
if(array[i] != firstNumber)
requiredArray[i] = array[i];
}
for(int i =0;i<array.length;i++)
{
if(i<numberOfOccurances)
requiredArray[i]= firstNumber;
}
//Print Output
for (int i = 0; i<requiredArray.length; i++)
System.out.print(requiredArray[i] + " ");
Output: 1 1 1 1 0 0 2 2
I was able to get desired output, but I'm not sure if this is the best way to solve my problem?
This is a simple solution.
Change number if:
- the other is firstNumber
- the other is minor and is not the first number
int[] array = {1,1,0,1,2,2,0,0,3,2,1,0,0,1,2,3,2,3};
int firstNumber = 1, temp;
for(int i=0;i<array.length-1;i++) {
for(int j=i+1;j<array.length;j++) {
if( (array[j] < array[i] && array[i]!=firstNumber) || array[j]==firstNumber) {
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
There is a better way. In particular, your sorting algorithm is
Not optimal in complexity (O(n^2)), see well-known sorting algorithms for O(n * log n) alternatives (https://en.wikipedia.org/wiki/Sorting_algorithm see MergeSort and QuickSort for popular ones). In pratice, your language's standard lib will contain a good implementation anyway.
Working on n elements still. If you have many of "first", you'd actually only have to m = n - number_of_first elements and that can be done in O(m * log m).
Usually, the second point is not important. If it is, you might deal with few distinct elements and then it would also be a good idea to use something like for even better time compelxity https://en.wikipedia.org/wiki/Counting_sort
Ignoring the case with few distinct elements, I'd recommend you go over the array once and copy all elements that aren't "firstNumber" to another array. Then you sort this other array using a standard sorting algorithm - preferably the built-in one.
Finally, your output is array.length - otherArray.length times firstNumber followed by the content of the sorted otherArray
depending on your size of the numbers, you might want to count each occurance of a number and print out the number the number of times you counted it. This is a O(n) algorithm.
arrayToBeSorted = {collection of numbers}
firstNumber = arrayToBeSorted[0]
countArray = new int[max(arrayToBeSorted)]; //everything defaults to 0 in most languages
for i:= 0 -> N
countArray[arrayToBeSorted[i]]++;
for i:= 0 ->countArray[firstNumber]
print(firstNumber)
for i: = 0 ->countArray.length
if (i == firstNumber)
continue;
for j:= 0 -> countArray[i]
print(i)

How to find the number of elements in the array that are bigger than all elements after it?

I have a function that takes a one-dimensional array of N positive integers and returns the number of elements that are larger than all the next. The problem is exist a function to do it that in a better time? My code is the following:
int count(int *p, int n) {
int i, j;
int countNo = 0;
int flag = 0;
for(i = 0; i < n; i++) {
flag = 1;
for(j = i + 1; j < n; j++) {
if(p[i] <= p[j]) {
flag = 0;
break;
}
}
if(flag) {
countNo++;
}
}
return countNo;
}
My solution is O(n^2). Can it be done better?
You can solve this problem in linear time(O(n) time). Note that the last number in the array will always be a valid number that fits the problem definition. So the function will always output a value that will be greater than equal to 1.
For any other number in the array to be a valid number it must be greater than or equal to the greatest number that is after that number in the array.
So iterate over the array from right to left keeping track of the greatest number found till now and increment the counter if current number is greater than or equal to the greatest found till now.
Working code
int count2(int *p, int n) {
int max = -1000; //this variable represents negative infinity.
int cnt = 0;
int i;
for(i = n-1; i >=0; i--) {
if(p[i] >= max){
cnt++;
}
if(p[i] > max){
max = p[i];
}
}
return cnt;
}
Time complexity : O(n)
Space complexity : O(1)
It can be done in O(n).
int count(int *p, int n) {
int i, currentMax;
int countNo = 0;
currentMax = p[n-1];
for(i = n-1; i >= 0; i--) {
if(currentMax < p[i])
{
countNo ++;
currentMax = p[i];
}
}
return countNo;
}
Create an auxillary array aux:
aux[i] = max{arr[i+1], ... ,arr[n-1] }
It can be done in linear time by scanning the array from right to left.
Now, you only need the number of elements such that arr[i] > aux[i]
This is done in O(n).
Walk backwards trough the array, and keep track of the current maximum. Whenever you find a new maximum, that element is larger than the elements following.
Yes, it can be done in O(N) time. I'll give you an approach on how to go about it. If I understand your question correctly, you want the number of elements that are larger than all the elements that come next in the array provided the order is maintained.
So:
Let len = length of array x
{...,x[i],x[i+1]...x[len-1]}
We want the count of all elements x[i] such that x[i]> x[i+1]
and so on till x[len-1]
Start traversing the array from the end i.e. at i = len -1 and keep track of the largest element that you've encountered.
It could be something like this:
max = x[len-1] //A sentinel max
//Start a loop from i = len-1 to i = 0;
if(x[i] > max)
max = x[i] //Update max as you encounter elements
//Now consider a situation when we are in the middle of the array at some i = j
{...,x[j],....x[len-1]}
//Right now we have a value of max which is the largest of elements from i=j+1 to len-1
So when you encounter an x[j] that is larger than max, you've essentially found an element that's larger than all the elements next. You could just have a counter and increment it when that happens.
Pseudocode to show the flow of algorithm:
counter = 0
i = length of array x - 1
max = x[i]
i = i-1
while(i>=0){
if(x[i] > max){
max = x[i] //update max
counter++ //update counter
}
i--
}
So ultimately counter will have the number of elements you require.
Hope I was able to explain you how to go about this. Coding this should be a fun exercise as a starting point.

Optimizing this code to check sum

This is the problem, I'm trying to solve in SPOJ. I am getting time limit exceeded problem. I can't find a way to optimize the algorithm. Can you give me some tips.
Here is the problem:
Leonard had to find the number of continuous sequence of numbers such
that their sum is zero.
For example if the sequence is- 5, 2, -2, 5, -5, 9
There are 3 such sequences
2, -2
5, -5
2, -2, 5, -5
Since this is a golden opportunity for Leonard to rewrite the Roommate
Agreement and get rid of Sheldon's ridiculous clauses, he can't afford
to lose. So he turns to you for help. Don't let him down.
Input
First line contains T - number of test cases
Second line contains n - the number of elements in a particular test
case.
Next line contain n elements, ai (1<=i<= n) separated by spaces.
Output
The number of such sequences whose sum if zero.
Constraints
1<=t<=5
1<=n<=10^6
-10<= ai <= 10
Below is my code:
#include<stdio.h>
main()
{
int t, j, k, l, sum;
long long int num, out = 0;
long long int ai[1000001];
scanf("%d",&t);
while(t--)
{
for(j=0;j<=num;j++)
{
scanf("%lld",&ai[j]);
}
for(l=0;l<=num;l++)
{
for(k=l; k<=num; k++)
{
if(sum == 0)
{
num++;
}
else
{
sum = sum + ai[k];
}
}
printf("%d", &num);
}
}
return 0;
}
Let S[i] be the sum of elements from index 0 to index i (prefix sum). Set S[-1] = 0.
You can observe that if a sequence from index i to index j (j > i) sums to 0, S[j] - S[i-1] = 0, or S[j] = S[i-1].
In order to solve this problem, just keep a mapping of the value of S[i] (i=-1..n-1) to the frequency of the sum. If a particular sum recurs k times, you can have k choose 2 ways to pairing up the indices to create distinct sequences. You can obtain the total number of sequences by summing up all the ways to pair up the indices, by going through all the keys and check the frequency at the end.
The implementation of the map should be at most O(log n) for insert and update operations. This will allow to you to solve the problem with overall complexity of O(n log n): prefix sum O(n), insert/update the map O(n log n), going through the whole map to sum up the result O(n).
Pseudocode:
a[n] // Array of elements
m = new Map[Int->Int] // Frequency mapping
s = 0 // Prefix sum
m[s] += 1
for (i = 0; i < n; i++) {
s += a[i] // Prefix sum of array of elements a
m[s] += 1 // Increment frequency of the prefix sum by 1
}
out = 0
// Go through all key values in the map
m.traverse(function (key, value) {
// Add the number of pairs of indices that has the same prefix sum
out += value * (value - 1) / 2
})
return out
This is my solution, it is similar to nhahtdh, but the time complexity is O(n)
Pseudocode:
int pos[10000001];
int neg[10000001];
int sum = 0;
int result = 0;
for(int i = 0; i < arr.length; i++){
sum += arr[i];
if(sum > 0){
result += pos[sum];
pos[sum]++;
}else{
result += neg[-sum];
neg[-sum]++;
}
}
return result;

maximum contiguous sum in a circular buffer

I have a program to determine the largest contiguous sum in an array, but want to extend it to work with circular arrays. Is there an easier way to do that than doubling the single array and calling my function to find the largest sum over all n-length arrays in the 2n length array?
See the following link :
It solves a problem using Kadane Algorithem.
http://www.geeksforgeeks.org/maximum-contiguous-circular-sum/
I think the solution by #spinning_plate is wrong. Ca you please test it for the given cases.
int arr[] = {-3, 6, 2, 1, 7, -8, 13, 0};
Your approach returns 21.
Actual solution can be start from 6th index(i.e. 13 value) .. and end to 4th index(i.e. 7 value). Since array is circular we can take continuous series from 6th index to 7th index and from 0th index to 4th index.
The actual answer for the above case is : 26
Well, you don't have to actually double the array. You can just emulate it by indexing your existing array modulo n, or by just iterating over it twice. Depending on the size of your array and cache behavior, this should be at most a factor of two slower than the algorithm for the noncircular array.
For the given problem,
We will apply kadane algorithm and we will also find the subset which will have maximum negative value.If the maximum negative value is removed that will give the sum of the remaining array in circular order.If that sum is greater than maximum sum then maximum sum will be sum in circular order.
Complexity for the algorithm is O(n).
Eg:- arr[i]={10,-3,-4,7,6,5,-4,-1}
Ans: max_sum=7+6+5+(-4)+(-1)+10
Removed_set={-3,-4}
int find_maxsum(int arr[],int n)
{
int i=0;
int total=0;
int maxa=0;
int mini=0;
int min_sum=0;
int max_sum=0;
while(i<n)
{
maxa=maxa+arr[i];
total=total+arr[i];
mini=mini+arr[i];
if(maxa>max_sum)
max_sum=maxa;
if(mini<min_sum)
min_sum=mini;
if(maxa<0)
maxa=0;
if(mini>=0)
mini=0;
}
if(total-min_sum>max_sum)
max_sum=total-min_sum;
return max_sum;
}
I assume you are using the O(n) algorithm that continues adding to the sum, keeping track of the maximum, only restarting if you sum to a negative number. The only thing you need to do to capture the case of circular arrays is to apply the same principle to the circular aspect. When you reach the end of the array in the original algorithm, keep looping around to the start until you go below the maximum or hit the beginning of the current range (I think this is impossible, though, because if the solution was the full array, we sould have seen this on the first pass), in which case you're done.
max_start=0; max_end =0; maxv = 0; sum 0;
for i in range(arr):
sum+= arr[i];
if sum<0:
sum=0; max_start =i;
if maxv<sum:
maxv=sum; max_end = i;
#seocnd pass
for i in range(max_start):
sum+= arr[i];
if sum<0:
break;
if maxv<sum:
maxv=sum;max_end = i;
Correct code based on nikhil's idea: elements of the minimum sum sub-array cannot appear in the final wrapping-or-not maximum sum sub-array.
public int maxSum(int[] arr) {
if (arr.length == 0) return 0;
int sum = 0;
int min = Integer.MAX_VALUE;
int eix = 0;
for (int i = 0; i < arr.length; i++) {
sum = sum + arr[i] < arr[i] ? sum + arr[i] : arr[i];
if (sum < min) {
min = sum;
eix = i;
}
}
int max = 0;
sum = 0;
for (int i = eix; i < arr.length + eix; i++) {
int ix = i < arr.length ? i : i - arr.length;
sum = sum + arr[ix] > arr[ix] ? sum + arr[ix] : arr[ix];
max = max > sum ? max : sum;
}
return max;
}
This code returns the correct answer even if all numbers are negative e.g., {-1, -2, -3}.
will return -1;
public static int maxSubarraySumCircular(int[] A) {
int maxSum = Arrays.stream(A).max().getAsInt();
if (maxSum < 0)
return maxSum;
int maxKadane = KadaneAlgorithm(A);
int maxWrap = 0;
for (int i = 0; i < A.length; i++) {
maxWrap += A[i];
A[i] = -A[i];
}
maxWrap = maxWrap + KadaneAlgorithm(A);
return maxWrap > maxKadane ? maxWrap : maxKadane;
}
private static int KadaneAlgorithm(int[] A) {
int maxSoFar = 0;
int maxEndingHere = 0;
for (int i = 0; i < A.length ; i++) {
maxEndingHere = maxEndingHere + A[i];
if (maxEndingHere < 0 )
maxEndingHere = 0;
if(maxSoFar < maxEndingHere)
maxSoFar = maxEndingHere;
}
return maxSoFar;
}

Resources