Related
I'm trying to solve this problem:
Given an array of positive integers, and an integer Y, you are allowed to replace at most Y array-elements with lesser values. Your goal is for the array to end up with as large a subset of identical values as possible. Return the size of this largest subset.
The array is originally sorted in increasing order, but you do not need to preserve that property.
So, for example, if the array is [10,20,20,30,30,30,40,40,40] and Y = 3, the result should be 6, because you can get six 30s by replacing the three 40s with 30s. If the array is [20,20,20,40,50,50,50,50] and Y = 2, the result should be 5, because you can get five 20s by replacing two of the 50s with 20s.
Below is my solution with O(nlogn) time complexity. (is that right?) I wonder if I can further optimize this solution?
Thanks in advance.
public class Nails {
public static int Solutions(int[] A, int Y) {
int N = A.length;
TreeMap < Integer, Integer > nailMap = new TreeMap < Integer, Integer > (Collections.reverseOrder());
for (int i = 0; i < N; i++) {
if (!nailMap.containsKey(A[i])) {
nailMap.put(A[i], 1);
} else {
nailMap.put(A[i], nailMap.get(A[i]) + 1);
}
}
List < Integer > nums = nailMap.values().stream().collect(Collectors.toList());
if (nums.size() == 1) {
return nums.get(0);
}
//else
int max = nums.get(0);
int longer = 0;
for (int j = 0; j < nums.size(); j++) {
int count = 0;
if (Y < longer) {
count = Y + nums.get(j);
} else {
count = longer + nums.get(j);
}
if (max < count) {
max = count;
}
longer += nums.get(j);
}
return max;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
String[] input = scanner.nextLine().replaceAll("\\[|\\]", "").split(",");
System.out.println(Arrays.toString(input));
int[] A = new int[input.length - 1];
int Y = Integer.parseInt(input[input.length - 1]);
for (int i = 0; i < input.length; i++) {
if (i < input.length - 1) {
A[i] = Integer.parseInt(input[i]);
} else {
break;
}
}
int result = Solutions(A, Y);
System.out.println(result);
}
}
}
A C++ implementation would like the following where A is the sorted pin size array and K is the number of times the pins can be hammered.
{1,1,3,3,4,4,4,5,5}, K=2 should give 5 as the answer
{1,1,3,3,4,4,4,5,5,6,6,6,6,6,6}, K=2 should give 6 as the answer
int maxCount(vector<int>& A, int K) {
int n = A.size();
int best = 0;
int count = 1;
for (int i = 0; i < n-K-1; i++) {
if (A[i] == A[i + 1])
count = count + 1;
else
count = 1;
if (count > best)
best = count;
}
int result = max(best+K, min(K+1, n));
return result;
}
Since the array is sorted to begin with, a reasonably straightforward O(n) solution is, for each distinct value, to count how many elements have that value (by iteration) and how many elements have a greater value (by subtraction).
public static int doIt(final int[] array, final int y) {
int best = 0;
int start = 0;
while (start < array.length) {
int end = start;
while (end < array.length && array[end] == array[start]) {
++end;
}
// array[start .. (end-1)] is now the subarray consisting of a
// single value repeated (end-start) times.
best = Math.max(best, end - start + Math.min(y, array.length - end));
start = end; // skip to the next distinct value
}
assert best >= Math.min(y + 1, array.length); // sanity-check
return best;
}
First, iterate through all the nails and create a hash H that stores the number of nails for each size. For [1,2,2,3,3,3,4,4,4], H should be:
size count
1 : 1
2 : 2
3 : 3
4 : 3
Now create an little algorithm to evaluate the maximum sum for each size S, given Y:
BestForSize(S, Y){
total = H[S]
while(Y > 0){
S++
if(Y >= H[S] and S < biggestNailSize){
total += H[S]
Y -= H[S]
}
else{
total += Y
Y = 0
}
}
return total;
}
Your answer should be max(BestForSize(0, Y), BestForSize(1, Y), ..., BestForSize(maxSizeOfNail, Y)).
The complexity is O(n²). A tip to optimize is to start from the end. For example, after you have the maximum value of nails in the size 4, how can you use your answer to find the maximum number of size 3?
Here is my java implementation: First I build a reversed map of each integer and its occurence for example {1,1,1,1,3,3,4,4,5,5} would give {5=2, 4=2, 3=2, 1=4}, then for each integer I calculate the max occurence that we can get of it regarding the K and the occurences of the highest integers in the array.
public static int ourFunction(final int[] A, final int K) {
int length = A.length;
int a = 0;
int result = 0;
int b = 0;
int previousValue = 0;
TreeMap < Integer, Integer > ourMap = new TreeMap < Integer, Integer > (Collections.reverseOrder());
for (int i = 0; i < length; i++) {
if (!ourMap.containsKey(A[i])) {
ourMap.put(A[i], 1);
} else {
ourMap.put(A[i], ourMap.get(A[i]) + 1);
}
}
for (Map.Entry<Integer, Integer> entry : ourMap.entrySet()) {
if( a == 0) {
a++;
result = entry.getValue();
previousValue = entry.getValue();
} else {
if( K < previousValue)
b = K;
else
b = previousValue;
if ( b + entry.getValue() > result )
result = b + entry.getValue();
previousValue += entry.getValue();
}
}
return result;
}
Since the array is sorted, we can have an O(n) solution by iterating and checking if current element is equals to previous element and keeping track of the max length.
static int findMax(int []a,int y) {
int n = a.length,current = 1,max = 0,diff = 0;
for(int i = 1; i< n; i++) {
if(a[i] == a[i-1]) {
current++;
diff = Math.min(y, n-i-1);
max = Math.max(max, current+diff);
}else {
current = 1;
}
}
return max;
}
given int array is not sorted than you should sort
public static int findMax(int []A,int K) {
int current = 1,max = 0,diff = 0;
List<Integer> sorted=Arrays.stream(A).sorted().boxed().collect(Collectors.toList());
for(int i = 1; i< sorted.size(); i++) {
if(sorted.get(i).equals(sorted.get(i-1))) {
current++;
diff = Math.min(K, sorted.size()-i-1);
max = Math.max(max, current+diff);
}else {
current = 1;
}
}
return max;
}
public static void main(String args[]) {
List<Integer> A = Arrays.asList(3,1,5,3,4,4,3,3,5,5,5,1);
int[] Al = A.stream().mapToInt(Integer::intValue).toArray();
int result=findMax(Al, 5);
System.out.println(result);
}
Hey guys can anyone help me in my problem about array index out of bound in my java code. Below is my full code having a problem.
public static double calculateMedian(double[] arr) {
double[] sortedArr = sortarr(arr);
double median;
if (arr.length % 2 == 0) {
int indexA = (arr.length-1) / 2;
int indexB = (arr.length) / 2;
median = ((double) (sortedArr[indexA] + sortedArr[indexB])) / 2; // this code has a error in index out of bounds.
}
else {
int index = (sortedArr.length - 1) / 2;
median = sortedArr[ index ];
}
return median;
}
This is my sorting fucntion
public static double[] sortarr(double[] arr) {
boolean performedSwap = true;
double modeValue;
while(performedSwap) {
performedSwap = false;
for (int i=0; i < arr.length-1; i++) {
if (arr[i] > arr[i+1]) {
modeValue = arr[i];
arr[i] = arr[i+1];
arr[i+1] = modeValue;
performedSwap = true;
}
}
}
return arr;
}
if arr.length = 0 (empty array) your code try to take sortedArr[0] and you have this exception.
You are clearly passing an empty array.
Add an extra condition before checking for arr.length % 2 == 0 to see if arr.length == 0.
Not sure what you would want to return as the median in that case.
The only case where the error can happen is if your array is empty. The median for empty array is none since there is no value to begin with.
public static double calculateMedian(double[] arr) {
double[] sortedArr = sortarr(arr);
double median;
if (arr.length == 0) {
return ; // return nothing if it is an empty array
} else if (arr.length % 2 == 0) {
int indexA = (arr.length-1) / 2;
int indexB = (arr.length) / 2;
median = ((double) (sortedArr[indexA] + sortedArr[indexB])) / 2; // this code has a error in index out of bounds.
} else {
int index = (sortedArr.length - 1) / 2;
median = sortedArr[ index ];
}
return median;
}
There is a problem in which two random integer arrays are given, in which numbers from 0 to 9 are present at every index (i.e. single digit integer is present at every index of both given arrays). I need to find the sum of the numbers represented by the input arrays and put the result in another array.
I believe everything is fine with my code as I execute it almost 50 to 60 times for different arrays. But when I submit it in my school's online judge it accepted only 4 test cases and rejected the other two. I can't figure out in which case it will give wrong output. Need a little help guys.
HERE IS MY CODE
public static int[] sumOfTwoArrays(int[] arr1, int[] arr2){
int size1 = arr1.length;
int size2 = arr2.length;
int carry = 0,sum,s,r;
if(size1 == size2) {
int arr3[] = new int[size1+1];
for(int i=arr1.length-1;i>=-1;i--) {
if(i==-1) {
arr3[i+1] = carry;
//System.out.println(i+1+" "+arr3[i+1]);
} else {
sum = arr1[i] + arr2[i];
if(sum>9) {
s =sum;
r = s % 10;
arr3[i+1] = carry + r;
carry = 1;
//System.out.println(i+" "+arr3[i]);
} else {
if(sum==9 && carry==1) {
s =sum+carry;
r = s % 10;
arr3[i+1] = r;
} else {
arr3[i+1] = sum+carry;
carry=0;
}
//System.out.println(i+" "+arr3[i]);
}
}
}
return arr3;
} else if (size1>size2) {
int arr3[] = new int[size1+1];
int diff = arr1.length - arr2.length;
for(int i=arr1.length-1;i>=-1;i--) {
if(i==-1) {
arr3[i+1] = carry;
} else {
if(i>=diff) {
sum = arr1[i] + arr2[i-diff];
if(sum>9) {
s =sum;
r = s % 10;
arr3[i+1] = carry + r;
carry = 1;
} else {
if(sum==9 && carry==1) {
s =sum+carry;
r = s % 10;
arr3[i+1] = r;
} else {
arr3[i+1] = sum+carry;
carry=0;
}
}
} // end of diff i
else {
arr3[i+1] = arr1[i];
carry = 0;
}
}
}
return arr3;
} else {
int arr3[] = new int[size2+1];
int diff = arr2.length - arr1.length;
for(int i=arr2.length-1;i>=-1;i--) {
if(i==-1) {
arr3[i+1] = carry;
} else {
if(i>=diff) {
sum = arr2[i] + arr1[i-diff];
if(sum>9) {
s =sum;
r = s % 10;
arr3[i+1] = carry + r;
carry = 1;
} else {
if(sum==9 && carry==1) {
s =sum+carry;
r = s % 10;
arr3[i+1] = r;
} else {
arr3[i+1] = sum+carry;
carry=0;
}
}
} // end of diff i
else {
arr3[i+1] = arr2[i];
carry = 0;
}
}
}
return arr3;
}
}
Sample input:
int[] arr1 = {8,5,3,9,6};
int[] arr2 = {3,3,3,3,3};
Sample output:
{1,1,8,7,2,9}
Sample input:
int[] arr1 = {8,5,3,9,6};
int[] arr2 = {1,0,5};
Sample output:
{0,8,5,5,0,1}
Well, I have this algorith based on Eran solution (was working to fixe the bug he since corrected), I will shared it since I use less arrays.
public static int[] sum(int[] arr1, int[] arr2){
int carry = 0;
int sum = 0;
int len1 = arr1.length;
int len2 = arr2.length;
int len = Math.max(len1, len2);
int arr3[] = new int[len + 1];
for (int i = 1; i <= len; i++) {
sum =
(len1 - i >= 0 ? arr1[len1-i] : 0)
+ (len2 - i >= 0 ? arr2[len2-i] : 0)
+ carry;
arr3[len-i+1] = sum%10;
carry = sum/10;
}
arr3[0] = carry;
return arr3;
}
The usage of ternary operator is still readable so I find this a good solution.
For a short explanation, we read the arrays from the end, using i to read from right to left but based on the length of the arrays. The ternary operation is used in case of different array size.
EDIT :
Your algorithm doesn't manage correctly the carry value with different sized array.
185 + 16 gives 101.
Simply because you set the values like :
arr3[i+1] = arr1[i];
So you forgot the carry that could occurs in the last operation.
This code is way more complicated than it has to be, which increases the chances of it containing bugs hard to detect.
You don't have to implement the algorithm 3 times (based of whether the first array is smaller, larger or equal to the second array). You can implement it once for two equal sized arrays whose size is Math.max(arr1.length,arr2.length).
That would eliminate 2/3 of your code.
int len = Math.max(arr1.length,arr2.length);
int[] arr11 = new int[len];
int[] arr22 = new int[len];
int arr3[] = new int[len+1];
for(int i=len-1;i>=-1;i--) {
if (i>=len-arr1.length)
arr11[i]=arr1[i-(len-arr1.length)];
if (i>=len-arr2.length)
arr22[i]=arr2[i-(len-arr2.length)];
// now you can use arr11[i] and arr22[i] instead of arr1[i] and arr2[i]
...
}
Besides, instead of sum = arr1[i] + arr2[i]; I suggest you add the carry immediately - sum = arr11[i] + arr22[i] + carry;. Now you only have to check once whether sum > 9.
if(i==-1) {
arr3[i+1] = carry;
} else {
sum = arr11[i] + arr22[i] + carry;
if(sum>9) {
arr3[i+1] = sum % 10;
carry = 1;
} else {
arr3[i+1] = sum;
carry = 0;
}
}
Combining the two snippets, you'll get :
int carry = 0;
int sum = 0;
int len = Math.max(arr1.length,arr2.length);
int[] arr11 = new int[len];
int[] arr22 = new int[len];
int arr3[] = new int[len+1];
for(int i=len-1;i>=-1;i--) {
if(i==-1) {
arr3[i+1] = carry;
} else {
if (i>=len-arr1.length)
arr11[i]=arr1[i-(len-arr1.length)];
if (i>=len-arr2.length)
arr22[i]=arr2[i-(len-arr2.length)];
sum = arr11[i] + arr22[i] + carry;
if(sum>9) {
arr3[i+1] = sum % 10;
carry = 1;
} else {
arr3[i+1] = sum;
carry = 0;
}
}
}
return arr3;
EDIT :
I had a small bug. I was adding 0s in the least significant digits of the smaller array (which are the high indices) instead of the most significant bits (the low indices), which made the result wrong if the arrays had different lengths. I fixed it, though now the part that copies the elements from the original arrays to arr11 and arr22 is less readable.
If that leading 0 in your second sample output is not necessary, you can also use a different approach by transforming the input beforehand, e.g. with a function as follows:
static Integer toNumber(int[] arr) {
return Integer.valueOf(Arrays.stream(arr)
.mapToObj(Integer::toString)
.collect(Collectors.joining()));
}
That way you can just sum up your arrays as if they would be normal integers:
Integer sum = toNumber(arr1) + toNumber(arr2);
Transforming that back to an array can be done as follows:
int[] sumArray = sum.toString().chars()
.map(operand -> Character.digit(operand, 10))
.toArray();
But you don't have that leading 0 now in your output. That code uses Java 8, but the same is also writable without streams (but untested):
static Integer toNumber(int[] arr) {
StringBuilder integerStrBuilder = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
integerStrBuilder.append(Integer.toString(arr[i]));
}
return Integer.valueOf(integerStrBuilder.toString());
}
and for the array:
char[] characters = sum.toString().toCharArray();
int[] sumArray = new int[characters.length];
for (int j = 0; j < characters.length; j++) {
sumArray[j] = Characters.digit(characters[j], 10);
}
int[] firstArray = {1,8,8,8, 8};
int[] secondArray = {1,8,9};
String diffstring1 = "", diffstring2 = "";
for (int i = 0; i < firstArray.length; i++) {
diffstring1 = diffstring1 + String.valueOf(firstArray[i]);
}
for (int i = 0; i < secondArray.length; i++) {
diffstring2 = diffstring2 + String.valueOf(secondArray[i]);
}
int diff = Integer.parseInt(diffstring1) + Integer.parseInt(diffstring2);
String diifffinal = String.valueOf(diff);
int[] third = new int[diifffinal.length()];
for (int j = 0; j < diifffinal.length(); j++) {
char abc = diifffinal.charAt(j);
third[j] = Character.getNumericValue(abc);
Log.d(TAG, "onCreate:---> " + third[j]);
}
It kinda works
int[] arr1 = {1, 2, 3, 4, 5, 6}, arr2 = {3, 5, 2, 9, 0};
int[] output = new int[Math.max(arr1.length, arr2.length)];
int num1 = 0, num2 = 0;
for (int value : arr1) {
num1 = (num1 * 10) + value;
}
for (int i : arr2) {
num2 = (num2 * 10) + i;
}
int result = (num1 + num2), k = output.length - 1;
while(result > 0){
output[k] = (result % 10);
result = result / 10;
k--;
}
if(k>0){
output[k] = 0;
k--;
}
for(int value: output){
System.out.print(value + " ");
}
}
I am trying to use java to pass an array to get the mean, median,mode , max an min in java. I am currently having an issue passing the array to a function and return its value so i can output the results. I believe i have the loops correct to solve the mean median and mode but i cannot get them to send and receive as wanted. How can I pass the array and send back the values needed?
UPDATE: i have updated the code it will compile and i can input the number of years but i get several errors following after that. it is also not printing the outputs
Exception in thread "main" java.util.UnknownFormatConversionException: Conversion = 'i'
at java.util.Formatter$FormatSpecifier.conversion(Formatter.java:2646)
at java.util.Formatter$FormatSpecifier.(Formatter.java:2675)
at java.util.Formatter.parse(Formatter.java:2528)
at java.util.Formatter.format(Formatter.java:2469)
at java.io.PrintStream.format(PrintStream.java:970)
at java.io.PrintStream.printf(PrintStream.java:871)
at la5cs1110_woodspl_03.pkg17.pkg2016.La5cs1110_WoodsPl_03172016.main(La5cs1110_WoodsPl_03172016.java:56)
Java Result: 1
public static void main(String[] args) {
int i;
List<Double> hArray = new ArrayList<>();
int nYears = 0, y = 0;
double rMax = 0.00,rMin = 100.00;
//get input check if between 1-80
while(y == 0){
String userData = JOptionPane.showInputDialog
("Enter number of years");
nYears = Integer.parseInt(userData);
if (nYears > 1 && nYears <= 80 )
y = 1;
}
y = 0;
while(y <= nYears){
for(i = 0; i < 12; i++){
Random rand = new Random();
double rNum = rand.nextFloat() * (rMax - rMin) + rMin;
hArray.add(rNum);
}
double mean = getMean (hArray);
double median = getMedian (hArray);
double mode = getMode (hArray);
double max = getMaxValue(hArray);
double min = getMinValue (hArray);
System.out.printf("In year %i the Mean = %d , mode = %d, median = %d," +
" max = %d, min = %d", y , mean, median, mode, max, min);
y++;
}
}
private static double getMean(List<Double> hArray) {
double sum = 0;
for (int i = 0; i < hArray.size(); i++) {
sum += hArray.get(i);
}
return sum / hArray.size();
}
//Median
private static double getMedian(List<Double> hArray) {
int middle = hArray.size()/2;
if (hArray.size() %2 == 1) {
return hArray.get(middle);
} else {
return (hArray.get(middle-1) + hArray.get(middle)) / 2.0;
}
}
//Mode
public static double getMode(List<Double> hArray) {
double maxValue = 0, maxCount = 0;
for (int i = 0; i < hArray.size(); ++i) {
int count = 0;
for (int j = 0; j < hArray.size(); ++j) {
if (hArray.get(j) == hArray.get(i)) ++count;
}
if (count > maxCount) {
maxCount = count;
maxValue = hArray.get(i);
}
}
return maxValue;
}
public static double getMaxValue(List<Double> hArray){
double maxValue = hArray.get(0);
for(int i=1;i < hArray.size();i++){
if(hArray.get(i) > maxValue){
maxValue = hArray.get(i);
}
}
return maxValue;
}
public static double getMinValue(List<Double> hArray){
double minValue = hArray.get(0);
for(int i=1;i<hArray.size();i++){
if(hArray.get(i) < minValue){
minValue = hArray.get(i);
}
}
return minValue;
}
}
Your hArray is a List. You should convert it to an array first.
getMean(hArray.toArray)
Check out this.
This does not compile, you try to pass a Double to a method, which expects a double[]. So you have to change the parameter of your methods and use a List and just pass in the hArray (see Tibrogargan answer - i.e., you would have to modify each of your implementations) or do the following:
create a Double[]
Double[] hArray2 = hArray.toArray(new Double[hArray.size()]);
change your methods' signature, so that they expect an Double[]
private static double getMean(Double[] hArray) { ...}
pass hArray2 instead of hArray
double mean = getMean(hArray2);
// ...
That should be it.
Replace the section where you're trying to pass a single element from the array to your statistics functions with calls using the whole array and change the signature of the calls so they take a List<Double> param, not a double[]. Something like this:
double mean = getMean (hArray);
double median = getMedian (hArray);
double mode = getMode (hArray);
double max = getMaxValue(hArray);
double min = getMinValue (hArray);
//Mean
private static double getMean(List<Double> hArray) {
double sum = 0;
for (int i = 0; i < hArray.size(); i++) {
sum += hArray.get(i);
}
return sum / hArray.size();
}
See also: How do you calculate the variance, median, and standard deviation in C++ or Java?
Fix for median:
Copied directly from this above link with some minor modifications to use a List as a param
public Double median(List<Double> list)
{
Double[] array = list.toArray(new Double[list.size()]);
Arrays.sort(data);
if (data.length % 2 == 0)
{
return (data[(data.length / 2) - 1] + data[data.length / 2]) / 2.0;
}
else
{
return data[data.length / 2];
}
}
Fix for mode:
public Double mode(List<Double> list)
{
java.util.TreeMap<Double,Integer> map = new java.util.TreeMap<>();
Double maxVal = null;
int maxCount = 0;
for (Double d : list) {
int count = 0;
if (map.containsKey(d)) {
count = map.get(d) + 1;
} else {
count = 1;
}
map.put(d, count);
if (count > maxCount) {
maxVal = d;
maxCount = count;
}
}
return maxVal;
}
In the codility test NumberOfDiscIntersections I am getting perf 100% and correctness 87% with the one test failing being
overflow
arithmetic overflow tests
got -1 expected 2
I can't see what is causing that given that I am using long which is 64-bit. And even if I can get it to 100% perf 100% correctness I am wondering if there is a better way to do this that is not as verbose in Java.
edit: figured out a much better way to do with with two arrays rather than a pair class
// you can also use imports, for example:
import java.util.*;
// you can use System.out.println for debugging purposes, e.g.
// System.out.println("this is a debug message");
class Solution {
public int solution(int[] A) {
int j = 0;
Pair[] arr = new Pair[A.length * 2];
for (int i = 0; i < A.length; i++) {
Pair s = new Pair(i - A[i], true);
arr[j] = s;
j++;
Pair e = new Pair(i + A[i], false);
arr[j] = e;
j++;
}
Arrays.sort(arr, new Pair(0, true));
long numIntersect = 0;
long currentCount = 0;
for (Pair p: arr) {
if (p.start) {
numIntersect += currentCount;
if (numIntersect > 10000000) {
return -1;
}
currentCount++;
} else {
currentCount--;
}
}
return (int) numIntersect;
}
static private class Pair implements Comparator<Pair> {
private long x;
private boolean start;
public Pair(long x, boolean start) {
this.x = x;
this.start = start;
}
public int compare(Pair p1, Pair p2) {
if (p1.x < p2.x) {
return -1;
} else if (p1.x > p2.x) {
return 1;
} else {
if (p1.start && p2.start == false) {
return -1;
} else if (p1.start == false && p2.start) {
return 1;
} else {
return 0;
}
}
}
}
}
Look at this line:
Pair s = new Pair(i + A[i], true);
This is equivalent with Pair s = new Pair((long)(i + A[i]) , true);
As i is integer, and A[i] is also integer, so this can cause overflow, as value in A[i] can go up to Integer.MAX_VALUE, and the cast to long happened after add operation is completed.
To fix:
Pair s = new Pair((long)i + (long)A[i], true);
Note: I have submitted with my fixed and got 100%
https://codility.com/demo/results/demoRRBY3Q-UXH/
My todays solution. O(N) time complexity. Simple assumption that number of availble pairs in next point of the table is difference between total open circle to that moment (circle) and circles that have been processed before. Maybe it's to simple :)
public int solution04(int[] A) {
final int N = A.length;
final int M = N + 2;
int[] left = new int[M]; // values of nb of "left" edges of the circles in that point
int[] sleft = new int[M]; // prefix sum of left[]
int il, ir; // index of the "left" and of the "right" edge of the circle
for (int i = 0; i < N; i++) { // counting left edges
il = tl(i, A);
left[il]++;
}
sleft[0] = left[0];
for (int i = 1; i < M; i++) {// counting prefix sums for future use
sleft[i]=sleft[i-1]+left[i];
}
int o, pairs, total_p = 0, total_used=0;
for (int i = 0; i < N; i++) { // counting pairs
ir = tr(i, A, M);
o = sleft[ir]; // nb of open till right edge
pairs = o -1 - total_used;
total_used++;
total_p += pairs;
}
if(total_p > 10000000){
total_p = -1;
}
return total_p;
}
int tl(int i, int[] A){
int tl = i - A[i]; // index of "begin" of the circle
if (tl < 0) {
tl = 0;
} else {
tl = i - A[i] + 1;
}
return tl;
}
int tr(int i, int[] A, int M){
int tr; // index of "end" of the circle
if (Integer.MAX_VALUE - i < A[i] || i + A[i] >= M - 1) {
tr = M - 1;
} else {
tr = i + A[i] + 1;
}
return tr;
}
My take on this, O(n):
public int solution(int[] A) {
int[] startPoints = new int[A.length];
int[] endPoints = new int[A.length];
int tempPoint;
int currOpenCircles = 0;
long pairs = 0;
//sum of starting and end points - how many circles open and close at each index?
for(int i = 0; i < A.length; i++){
tempPoint = i - A[i];
startPoints[tempPoint < 0 ? 0 : tempPoint]++;
tempPoint = i + A[i];
if(A[i] < A.length && tempPoint < A.length) //first prevents int overflow, second chooses correct point
endPoints[tempPoint]++;
}
//find all pairs of new circles (combinations), then make pairs with exiting circles (multiplication)
for(int i = 0; i < A.length; i++){
if(startPoints[i] >= 2)
pairs += (startPoints[i] * (startPoints[i] - 1)) / 2;
pairs += currOpenCircles * startPoints[i];
currOpenCircles += startPoints[i];
currOpenCircles -= endPoints[i];
if(pairs > 10000000)
return -1;
}
return (int) pairs;
}
The explanation to Helsing's solution part:
if(startPoints[i] >= 2) pairs += (startPoints[i] * (startPoints[i] - 1)) / 2;
is based on mathematical combinations formula:
Cn,m = n! / ((n-m)!.m!
for pairs, m=2 then:
Cn,2 = n! / ((n-2)!.2
Equal to:
Cn,2 = n.(n-1).(n-2)! / ((n-2)!.2
By simplification:
Cn,2 = n.(n-1) / 2
Not a very good performance, but using streams.
List<Long> list = IntStream.range(0, A.length).mapToObj(i -> Arrays.asList((long) i - A[i], (long) i + A[i]))
.sorted((o1, o2) -> {
int f = o1.get(0).compareTo(o2.get(0));
return f == 0 ? o1.get(1).compareTo(o2.get(1)) : f;
})
.collect(ArrayList<Long>::new,
(acc, val) -> {
if (acc.isEmpty()) {
acc.add(0l);
acc.add(val.get(1));
} else {
Iterator it = acc.iterator();
it.next();
while (it.hasNext()) {
long el = (long) it.next();
if (val.get(0) <= el) {
long count = acc.get(0);
acc.set(0, ++count);
} else {
it.remove();
}
}
acc.add(val.get(1));
}
},
ArrayList::addAll);
return (int) (list.isEmpty() ? 0 : list.get(0) > 10000000 ? -1 : list.get(0));
This one in Python passed all "Correctness tests" and failed all "Performance tests" due to O(n²), so I got 50% score. But it is very simple to understand. I just used the right radius (maximum) and checked if it was bigger or equal to the left radius (minimum) of the next circles. I also avoided to use sort and did not check twice the same circle. Later I will try to improve performance, but the problem here for me was the algorithm. I tried to find a very easy solution to help explain the concept. Maybe this will help someone.
def solution(A):
n = len(A)
cnt = 0
for j in range(1,n):
for i in range(n-j):
if(i+A[i]>=i+j-A[i+j]):
cnt+=1
if(cnt>1e7):
return -1
return cnt