5

I came across an interview questions and despite the fact I've been trying to solve it on my own I think I need some help.

I've got an array of integer numbers (positive and negative) representing points in space, the distance between two points is defined as abs(A[i]-A[j]) and I need to check that that distance is divisible by a given integer M.

So this is the situation :

Array : [-3 -2 1 0 8 7 1]

M = 3

abs(A[1]-A[2]) = 3 (for example and it's divisible by the integer)

The complexity should be O(N+M) and the space O(M)

Now these are the questions

1)I know there is a way to take in consideration al the couples without using the obvious solution with two "for loops" because the complexity would be N^2 which is undesirable, but I can't figure out how to do it

2)Complexity O(N+M) means that I need to use two for loops but not one inside another?(I mean two separate for loops), what I am trying to understand here it's if the complexity given can guide me toward the best algorithm I should use.

3)When the specification says that the integer name is M and the complexity is O(N+M), does this mean that there is a relation with the integer M and the complexity or it's only a case that the name is the same?

4)How to do it ?

I hope I have been clear enough, if not please let me know I wil try to explain myself better.

Ok Let's see if I understood correctly this is what I am trying so far :

int testCollection[7];

testCollection[0] = -3;
testCollection[1] = -2;
testCollection[2] = 1;
testCollection[3] = 0;
testCollection[4] = 8;
testCollection[5] = 7;
testCollection[6] = 1;

int arrayCollection[7];

for (unsigned int i = 0; i < 7; i++)
{
    arrayCollection[i] = 1000;
}

for (unsigned int i = 0; i < 7; i++)
{

    arrayCollection[i] = testCollection[i]%3;

} 

the arrayCollection now contains: [0, -2, 1, 0, 2, 1 ,1 ]

I did not understand what you mean for second time can you please be a little bit more specific? Imagine I am a child :)

cheers

p.s. I don't want to disturb you too much so if you prefer you can point me to some documentation I can read about the subject, googling I did not find much unfortunately.

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
Fra
  • 176
  • 2
  • 7
  • Regarding 3) When the integer -is- M, the complexity should be O(N+M). I.e., when M is larger you get more time than when M is small. – Atsby Apr 09 '15 at 06:56

5 Answers5

5

Two points have the same mod M if and only if they are an integer multiple of M apart. So make a integer array A of size mod M, and loop over the N integers, for each such integer N[i], effecting A[N[i] % M]++.

At the end, if any entry is >1, then there is at least one pair of integers that are a multiple of M apart.

To actually extract a solution (i.e., get the values that are k*M apart rather than simply knowing there are some), you can initialize entires of A to MAXINT, and assign the value to A the first time a particular mod M is seen. The second time you have the pair of values that work.

Atsby
  • 2,277
  • 12
  • 14
4

The key to solving this problem efficiently is realizing this one property:

"All points that are m (or a multiple of m) units away, will give the same modulo m result."

mod_res = num % m

This also implies the reverse, i.e. "all points that give the same modulo m result, are exactly a multiple of m units away."

Here is a small drawing to illustrate this with the numbers from your question:

Number line with modulo positions

Once we understand the relation between this modulo property and the relative distance between points, it becomes easy to code this out.

We just need to calculate the modulo_result of every number, and then group together numbers that have the same modulo_result.

def solution(array: List[int], m: int) -> int:
    # Maintain a hashmap of modulo_results and a list of numbers that resolve to that result
    modulo_results = defaultdict(list)
    for num in array:
        # Insert every number into the corresponding module_result list
        res = num % m
        modulo_results[res].append(num)
    
    # Now simply loop through every modulo_result list and find the longest one
    max_subset_size = 0
    max_subset = []
    for res, nums in modulo_results.items():
        if len(nums) > max_subset_size:
            max_subset_size = len(nums)
            max_subset = nums

    print(f"Max subset: {max_subset}")
    return max_subset_size

# print(solution([-3, -2, 1, 0, 8, 7, 1], m=3))
# Max subset: [-2, 1, 7, 1]
# 4

This solution takes O(n) time and O(n) extra space.

Aaron Alphonso
  • 336
  • 3
  • 6
  • May I ask how do you come up with the intuition? This problem becomes so trivial with the modulo property, but I have a hard time figure this out intuitively. – Jyhonn Dec 17 '22 at 16:11
  • I think its just experience with similar problems. Over time you develop the intuition of knowing where to apply what logic. – Aaron Alphonso Dec 19 '22 at 07:14
1

Implemented in kotlin kindly check below:

fun largestSubSetDivisible(array: Array<Int>, m: Int):Int {
    val result = Array(1) { 1 }
    largestSubSetDivisible(array, m = m, result = result)
    return result[0]
}

fun largestSubSetDivisible(
    array: Array<Int>,
    arr: IntArray = IntArray(array.size) { 0 },
    i: Int = 0,
    m: Int,
    result: Array<Int> = Array(1) { 1 }
) {
    fun checkDivisionPair(array: List<Int>, m: Int): Boolean {
        for (j in 0 until array.size - 1) {
            if ((array[j] - array[j + 1]) % m != 0)
                return false
        }
        return true
    }
    if (i == array.size) {
        val count = arr.count { it == 1 }
        if (result[0] < count) {
            val ar = array.filterIndexed { index, _ -> arr[index] == 1 }
            if (checkDivisionPair(ar, m))
                result[0] = count
        }
        return
    }
    arr[i] = 0
    largestSubSetDivisible(array, arr, i + 1, m, result)
    arr[i] = 1
    largestSubSetDivisible(array, arr, i + 1, m, result)
}
Anton Krug
  • 1,555
  • 2
  • 19
  • 32
0

This code runs in O(M+N) and O(M) extra space.

modulus = (num,n)=>{
    if(num<0){
        do{
            num += n;
        }while(num<0);
    }
    return num%n;
}
var arr=[-5,-2,4,7,10,0,8],m=3,mod=[];
for(var index=0;index<m;index++){
    mod[index]=0;
}
for(index=0;index<arr.length;index++){
    mod[modulus(arr[index],m)]++;
}
mod.sort(function(a,b){
    return a-b;
});
if(mod[m-1] > 1){
    console.log(mod[m-1]);
}

I came across this question recently in an interview. So, I assumed your question was a part of the same question I was asked.

Prakhar Mittal
  • 6,315
  • 1
  • 14
  • 31
0

C++ based Solution

int sol(std::vector<int> vec, int M)
{
    std::vector<int> mod(M);
    for (int p = 0; p < M; ++p) {
        mod[p] = 0;
    }
    for(int i : vec)
    {
        mod[(std::abs(vec[i]) % M)] = mod[(std::abs(vec[i]) % M)] + 1;
    }
    std::sort(mod.begin(), mod.end());
    return mod[M - 1];
}
int main( void )
{
    std::cout << sol({ -3, -2, 1, 0, 8, 7, 1 }, 3) << std::endl; // 4
    std::cout << sol({7, 1, 11, 8, 4, 10}, 8) << std::endl; // 2
    return 0;
}
Prashant
  • 121
  • 1
  • 3