4

I got this coding problem from a website. The question was as follows:

Two arrays are called similar if one can be obtained from another by swapping at most one pair of elements in one of the arrays.

Given two arrays, check whether they are similar.

Example

For A = [1, 2, 3] and B = [1, 2, 3], the output should be areSimilar(A, B) = true.

The arrays are equal, no need to swap any elements.

For A = [1, 2, 3] and B = [2, 1, 3], the output should be areSimilar(A, B) = true.

We can obtain B from A by swapping 2 and 1 in B.

For A = [1, 2, 2] and B = [2, 1, 1], the output should be areSimilar(A, B) = false.

Any swap of any two elements either in A or in B won't make A and B equal.

This is the solution I gave:

boolean areSimilar(int[] A, int[] B) {
    if(A.length != B.length) return false;

    int[] copyA = A, copyB = B;
    Arrays.sort(copyA); Arrays.sort(copyB); 
    int countSwap = 0;

    if(!Arrays.equals(copyA, copyB)) return false;

    for(int i = 0; i < A.length; i++) {
        if(A[i] != B[i]) countSwap++;
    }

    return (countSwap == 2 || countSwap == 0);
}

This code gave correct results for the following arrays:

  1. A: [1, 2, 3]
    B: [1, 2, 3]

  2. A: [1, 2, 3]
    B: [2, 1, 3]

  3. A: [1, 2, 2]
    B: [2, 1, 1]

  4. A: [1, 1, 4]
    B: [1, 2, 3]

  5. A: [1, 2, 3]
    B: [1, 10, 2]

  6. A: [2, 3, 1]
    B: [1, 3, 2]

But still the website displays "INCORRECT" every time I try to submit the code. It fails to pass two out of six hidden tests and I cannot figure out why. Is this the correct code? Is there any other, more easier way?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
FlarrowVerse
  • 197
  • 2
  • 13

4 Answers4

12

Your code is not working because you sorted the original arrays here ...

copyA = A, copyB = B;
Arrays.sort(copyA); Arrays.sort(copyB);

then you are comparing the sorted arrays instead of original arrays for checking if they can be converted using only one swap!!

You should do something like this...

boolean areSimilar(int[] A, int[] B) {
    if(A.length != B.length) return false;

    int countSwap = 0;
    int[] copyA = Arrays.copyOf(A, A.length);
    int[] copyB = Arrays.copyOf(B, B.length);

    // checking both contain the same elements...
    Arrays.sort(copyA); Arrays.sort(copyB); 
    if(!Arrays.equals(copyA, copyB)) return false; 

    // checking for min 2 swaps using original arrays...
    for(int i = 0; i < A.length; i++) {
        if(A[i] != B[i]) countSwap++;
    }

    return (countSwap == 2 || countSwap == 0);
}

more efficient solution ...

boolean areSimilar(int[] A, int[] B) {
  ArrayList<Integer> ids = new ArrayList<>();
  for (int i = 0; i < A.length; i++) {
    if ( A[i] != B[i] ) {
      ids.add(i);
    }
  }
  if (ids.size() == 0) {
    return true;
  }
  if (ids.size() != 2) {
    return false;
  }
  int id1 = ids.get(0);
  int id2 = ids.get(1);
  if (A[id1] == B[id2] && A[id2] == B[id1]) {
    return true;
  }
  return false;
}
Rajeev Singh
  • 3,292
  • 2
  • 19
  • 30
  • Yes I did do it. I used clone() instead of System.arraycopy() – FlarrowVerse Apr 25 '17 at 13:21
  • 1
    Now you are already using `Arrays`, you may consider `Arrays.copyOf()` for producing the copies (since Java 6). – Ole V.V. Apr 25 '17 at 13:23
  • You need to sort the copied arrays before you compare them. Also according to http://stackoverflow.com/questions/12157300/clone-or-arrays-copyof the consensus seems to be that `.clone()` is preferable to `Arrays.copyOf()`. – Klitos Kyriacou Apr 25 '17 at 13:40
  • @KlitosKyriacou thanks ... I completely missed that statement. – Rajeev Singh Apr 25 '17 at 13:43
  • @KlitosKyriacou, the answers saying that seem to be concerned with performance only. That is the least important criterion for selecting. I stand by my suggestion of `Arrays.copyOf()`. – Ole V.V. Apr 25 '17 at 17:40
  • 1
    @OleV.V. I also find `clone()` more readable, unless you wanted to change the length of the array copy. I guess it's a matter of personal preference. – Klitos Kyriacou Apr 26 '17 at 06:30
6

The problem in your program

Instead of creating and sorting copies of the arrays, you used assignment.

copyA = A

This means that copyA is still a reference to the original array, and therefore, the original arrays will be sorted when you try to count the swaps.

This means that when you try to check two arrays that have the same elements but many swaps, you'll get true when you are supposed to get false.

An array should be copied by:

  • A.clone()
  • Arrays.copyOf(A, A.length)
  • or copyA = new int[A.length]; System.arrayCopy(A,0,copyA,0,A.length);

Sets instead of sorting

Instead of copying the arrays, sorting and comparing, you can use sets. The reason that you are sorting is to check that both arrays have exactly the same elements. In that case, you can put the elements into sets and compare the sets, without sorting.

Set<Integer> setA = new HashSet<>(Arrays.asList(A));
Set<Integer> setB = new HashSet<>(Arrays.asList(B));
if ( ! setA.equals(setB) ) {
    return false;
}

The equals method for sets returns true if and only if the two sets contain exactly the same elements (order does not matter in sets).

The set approach will only work if your arrays are guaranteed not to have repeating values. Frequency maps could work for arrays with repetitions, but frankly, it would be unreadable.

A linear approach

Your approach takes O(n log n) because of the sorts, in addition to linear memory. In fact, the problem as it stands can be solved in linear time and constant memory.

  • Check that the lengths are equal. If not, return false.
  • Let ind be an array of two elements for indices of the positions where there are differences.
  • Loop over the arrays (as you do now) counting the differences.
  • If you encounter a difference, if countSwap < 2, put i in ind[countSwap], and then increment countSwap.
  • At the end of the loop, if countSwap is 0, return true.
  • If countSwap is 1 or greater than 2, return false.
  • If countSwap is 2, check the items at the indices that you kept. If A[ind[0]] == B[ind[1]] and A[ind[1]] == B[ind[0]], return true, otherwise return false.

Explanation

If there is 1 or 3 and above differences between the two arrays, then of course they are not "similar".

But if you have 2 differences exactly, these can be either because there are completely different values in those two places, or because there was a swap.

So you check if the 2 differences are because of a swap.

There is no need to sort. The only reason you are sorting is to see if the two arrays have exactly the same elements. But the number of differences can tell you that without sorting.

By the way, you can break out of the loop once countSwap reaches 3.

RealSkeptic
  • 33,993
  • 7
  • 53
  • 79
  • Sets won’t be precise enough for the comparison. Consider arrays [1, 1, 1, 3] and [1, 3, 3, 3]. Your sets will be the same and your swap count will be 2, so they might be deemed similar, but they are not. Using sorted arrays instead of sets will make sure to discover they cannot be similar. – Ole V.V. Apr 25 '17 at 17:34
  • @OleV.V. Right. I was about to put in a note about that. – RealSkeptic Apr 25 '17 at 18:54
0
areSimilar([4, 6, 3],[3, 4, 6]); // example arrays


boolean areSimilar(int[] a, int[] b) {
    
    boolean isSimular = false;
        int count = 0;
        int temp = 0;
        
        if(a.length!=b.length) { // check two arrays length
            return false;
        }
        
        for (int i = 0; i < a.length; i++) {
            if (a[i] == b[i]) {
                isSimular=true;
            } else {
                count++;
                if(count>1) {
                    isSimular =false;
                    return isSimular;
                } 
                for (int j = 0; j < a.length; j++) {
                    if(a[i]==b[j] && a[j]!=b[j]) {
                                                
                        temp=b[i];
                        b[i]=b[j];
                        b[j]=temp;
                        break;
                        
                        
                        
                        
                    }
                }
            }
        }

        
        return isSimular;
}
Arman
  • 1
  • 1
  • 1
    While this code may solve the question, [including an explanation](//meta.stackexchange.com/q/114762) of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please [edit] your answer to add explanations and give an indication of what limitations and assumptions apply. – Adrian Mole Oct 26 '21 at 12:44
-4
def areSimilar(a, b):
    n = 0
    if len(a)!=len(b):
        return False
    else:
        for i in range(len(a)):
            if a[i]!=b[i]:
                n+=1
                if n>2:
                    return False
            if a[i] not in b:
                return False
        print(n)
        return True
  • 2
    Thank you for contributing an answer. Would you kindly edit your answer to to include an explanation of your code? That will help future readers better understand what is going on, and especially those members of the community who are new to the language and struggling to understand the concepts. – STA Feb 28 '21 at 13:41