# Numbers that are bitwise AND of at least one non-empty sub-array

Given an array ‘arr’, the task is to find all possible integers each of which is the bitwise AND of at least one non-empty sub-array of ‘arr’.

Examples:

```Input: arr = {11, 15, 7, 19}
Output: [3, 19, 7, 11, 15]
3 = arr[2] AND arr[3]
19 = arr[3]
7 = arr[2]
11 = arr[0]
15 = arr[1]

Input: arr = {5, 2, 8, 4, 1}
Output: [0, 1, 2, 4, 5, 8]
0 = arr[3] AND arr[4]
1 = arr[4]
2 = arr[1]
4 = arr[3]
5 = arr[0]
8 = arr[2]
```

## Recommended: Please try your approach on {IDE} first, before moving on to the solution.

Naive Approach:

• An array of size ‘N’ contains N*(N+1)/2 sub-arrays. So, for small ‘N’, iterate over all possible sub-arrays and add each of their AND result to a set.
• Since, sets do not contain duplicates, it’ll store each value only once.
• Finally, print the contents of the set.

Below is the implementation of the above approach:

## Java

 `// Java implementation of the approach ` `import` `java.util.HashSet; ` `class` `CP { ` `    ``public` `static` `void` `main(String[] args) ` `    ``{ ` `        ``int``[] A = { ``11``, ``15``, ``7``, ``19` `}; ` `        ``int` `N = A.length; ` ` `  `        ``// Set to store all possible AND values. ` `        ``HashSet set = ``new` `HashSet<>(); ` `        ``int` `i, j, res; ` ` `  `        ``// Starting index of the sub-array. ` `        ``for` `(i = ``0``; i < N; ++i) ` ` `  `            ``// Ending index of the sub-array. ` `            ``for` `(j = i, res = Integer.MAX_VALUE; j < N; ++j) { ` `                ``res &= A[j]; ` ` `  `                ``// AND value is added to the set. ` `                ``set.add(res); ` `            ``} ` ` `  `        ``// The set contains all possible AND values. ` `        ``System.out.println(set); ` `    ``} ` `} `

## C#

 `// C# implementation of the approach  ` `using` `System; ` `using` `System.Collections.Generic; ` ` `  `class` `CP {  ` ` `  `    ``public` `static` `void` `Main(String[] args)  ` `    ``{  ` `        ``int``[] A = {11, 15, 7, 19};  ` `        ``int` `N = A.Length;  ` ` `  `        ``// Set to store all possible AND values.  ` `        ``HashSet<``int``> set1 = ``new` `HashSet<``int``>();  ` `        ``int` `i, j, res; ` ` `  `        ``// Starting index of the sub-array.  ` `        ``for` `(i = 0; i < N; ++i)  ` `        ``{ ` `            ``// Ending index of the sub-array.  ` `            ``for` `(j = i, res = ``int``.MaxValue; j < N; ++j) ` `            ``{  ` `                ``res &= A[j];  ` `                 `  `                ``// AND value is added to the set.  ` `                ``set1.Add(res);  ` `            ``} ` `             `  `        ``} ` `         `  `        ``// displaying the values ` `        ``foreach``(``int` `m ``in` `set1) ` `        ``{ ` `            ``Console.Write(m + ``" "``); ` `        ``} ` `         `  `    ``}  ` `}  `

Output:

```[3, 19, 7, 11, 15]
```

Time Complexity: O(N^2)

Efficient Approach: This problem can be solved efficiently by divide-and-conquer approach.

• Consider each element of the array as a single segment. (‘Divide’ step)
• Add the AND value of all the subarrays to the set.
• Now, for the ‘conquer’ step, keep merging consecutive subarrays and keep adding the additional AND values obtained while merging.
• Continue step 4, until a single segment containing the entire array is obtained.

Below is the implementation of the above approach:

 `// Java implementation of the approach ` `import` `java.util.*; ` ` `  `public` `class` `CP { ` `    ``static` `int` `ar[]; ` `    ``static` `int` `n; ` ` `  `    ``// Holds all possible AND results ` `    ``static` `HashSet allPossibleAND; ` ` `  `    ``// driver code ` `    ``public` `static` `void` `main(String[] args) ` `    ``{ ` `        ``ar = ``new` `int``[] { ``11``, ``15``, ``7``, ``19` `}; ` `        ``n = ar.length; ` ` `  `        ``allPossibleAND = ``new` `HashSet<>(); ``// initialization ` `        ``divideThenConquer(``0``, n - ``1``); ` ` `  `        ``System.out.println(allPossibleAND); ` `    ``} ` ` `  `    ``// recursive function which adds all ` `    ``// possible AND results to 'allPossibleAND' ` `    ``static` `Segment divideThenConquer(``int` `l, ``int` `r) ` `    ``{ ` ` `  `        ``// can't divide into  ` `        ``//further segments ` `        ``if` `(l == r) ` `        ``{ ` `            ``allPossibleAND.add(ar[l]); ` ` `  `            ``// Therefore, return a segment ` `            ``// containing this single element. ` `            ``Segment ret = ``new` `Segment(); ` `            ``ret.leftToRight.add(ar[l]); ` `            ``ret.rightToLeft.add(ar[l]); ` `            ``return` `ret; ` `        ``} ` ` `  `        ``// can be further divided into segments ` `        ``else` `{ ` `            ``Segment left  ` `                ``= divideThenConquer(l, (l + r) / ``2``); ` `            ``Segment right  ` `                ``= divideThenConquer((l + r) / ``2` `+ ``1``, r); ` ` `  `            ``// Now, add all possible AND results, ` `            ``// contained in these two segments ` ` `  `            ``/* ******************************** ` `            ``This step may seem to be inefficient  ` `            ``and time consuming, but it is not. ` `            ``Read the 'Analysis' block below for  ` `            ``further clarification. ` `            ``*********************************** */` `            ``for` `(``int` `itr1 : left.rightToLeft) ` `                ``for` `(``int` `itr2 : right.leftToRight) ` `                    ``allPossibleAND.add(itr1 & itr2); ` ` `  `            ``// 'conquer' step ` `            ``return` `mergeSegments(left, right); ` `        ``} ` `    ``} ` ` `  `    ``// returns the resulting segment after ` `    ``// merging segments 'a' and 'b' ` `    ``// 'conquer' step ` `    ``static` `Segment mergeSegments(Segment a, Segment b) ` `    ``{ ` `        ``Segment res = ``new` `Segment(); ` ` `  `        ``// The resulting segment will have ` `        ``// same prefix sequence as segment 'a' ` `        ``res.copyLR(a.leftToRight); ` ` `  `        ``// The resulting segment will have ` `        ``// same suffix sequence as segment 'b' ` `        ``res.copyRL(b.rightToLeft); ` ` `  `        ``Iterator itr; ` ` `  `        ``itr = b.leftToRight.iterator(); ` `        ``while` `(itr.hasNext()) ` `            ``res.addToLR(itr.next()); ` ` `  `        ``itr = a.rightToLeft.iterator(); ` `        ``while` `(itr.hasNext()) ` `            ``res.addToRL(itr.next()); ` ` `  `        ``return` `res; ` `    ``} ` `} ` ` `  `class` `Segment { ` ` `  `    ``// These 'vectors' will always  ` `    ``// contain atmost 30 values. ` `    ``ArrayList leftToRight  ` `        ``= ``new` `ArrayList<>(); ` `    ``ArrayList rightToLeft  ` `        ``= ``new` `ArrayList<>(); ` ` `  `    ``void` `addToLR(``int` `value) ` `    ``{ ` `        ``int` `lastElement  ` `            ``= leftToRight.get(leftToRight.size() - ``1``); ` ` `  `        ``// value decreased after AND-ing with 'value' ` `        ``if` `((lastElement & value) < lastElement) ` `            ``leftToRight.add(lastElement & value); ` `    ``} ` ` `  `    ``void` `addToRL(``int` `value) ` `    ``{ ` `        ``int` `lastElement  ` `            ``= rightToLeft.get(rightToLeft.size() - ``1``); ` ` `  `        ``// value decreased after AND-ing with 'value' ` `        ``if` `((lastElement & value) < lastElement) ` `            ``rightToLeft.add(lastElement & value); ` `    ``} ` ` `  `    ``// copies 'lr' to 'leftToRight' ` `    ``void` `copyLR(ArrayList lr) ` `    ``{ ` `        ``Iterator itr = lr.iterator(); ` `        ``while` `(itr.hasNext()) ` `            ``leftToRight.add(itr.next()); ` `    ``} ` ` `  `    ``// copies 'rl' to 'rightToLeft' ` `    ``void` `copyRL(ArrayList rl) ` `    ``{ ` `        ``Iterator itr = rl.iterator(); ` `        ``while` `(itr.hasNext()) ` `            ``rightToLeft.add(itr.next()); ` `    ``} ` `} `

Output:

```[19, 3, 7, 11, 15]
```

Analysis:

The major optimization in this algorithm is realizing that any array element can yield a maximum of 30 distinct integers (as 30 bits are needed to hold 1e9). Confused?? Let’s proceed step by step.

Let us start a sub-array from the ith element which is A[i]. As the succeeding elements are AND-ed with Ai, the result can either decrease or remain same (because a bit will never get changed from ‘0’ to ‘1’ after an AND operation).
In the worst case, A[i] can be 2^31 – 1 (all 30 bits will be ‘1’). As the elements are AND-ed, atmost 30 distinct values can be obtained because only a single bit might get changed from ‘1’ to ‘0’ in the worst case,
i.e. 111111111111111111111111111111 => 111111111111111111111111101111

So, for each ‘merge’ operation, these distinct values merge to form another set having atmost 30 integers.
Therefore, Worst Case Time Complexity for each merge can be O(30 * 30) = O(900).

Time Complexity: O(900*N*logN).

PS: The time complexity can seem too high, but in practice, the actual complexity lies around O(K*N*logN), where, K is much less than 900. This is because, the lengths of the ‘prefix’ and ‘suffix’ arrays are much less when ‘l’ and ‘r’ are quite close.

My Personal Notes arrow_drop_up