【數組】B061_LC_形成兩個異或相等數組的三元組數目(暴力 / 暴力優化 / 前綴和 / 異或規則)

一、Problem

Given an array of integers arr. We want to select three indices i, j and k where (0 <= i < j <= k < arr.length).

Let’s define a and b as follows:

a = arr[i] ^ arr[i + 1] ^ ... ^ arr[j - 1]
b = arr[j] ^ arr[j + 1] ^ ... ^ arr[k]

Note that ^ denotes the bitwise-xor operation.

Return the number of triplets (i, j and k) Where a == b.

Input: arr = [2,3,1,6,7]
Output: 4
Explanation: The triplets are (0,1,2), (0,2,2), (2,3,4) and (2,4,4)

二、Solution

方法一:暴力(超時)

class Solution {
    public int countTriplets(int[] arr) {
        int cnt = 0, N = arr.length;
        for (int i = 0; i < N-1; i++)
        for (int j = i+1; j < N; j++) {
            for (int k = j; k < N; k++) {
                int a = arr[i];
                for (int l = i+1; l <= j-1; l++)
                    a ^= arr[l];
                int b = arr[j];
                for (int l = j+1; l <= k; l++)
                    b ^= arr[l];
                if (a == b) cnt++;
            }
        }
        return cnt;
    }
}

複雜度分析

  • 時間複雜度:O(n4)O(n^4)
  • 空間複雜度:O(1)O(1)

方法二:優化

  • 其實不用顯式地將 a 異或上區間 [i,j1][i, j-1] 中的數,b 也不用,取而代之的是
  • 第一層循環先確定 aa,第二層循環先確定 bb,第三層循環開始將區間 [j,N][j, N] 的數異或上 bb
  • 第三層循環結束,代表從 b 已經枚舉完畢,那麼 a 就可以繼續異或上第二層循環中的數。

這樣可將複雜度降爲 O(n3)O(n^3),運算量大約爲 3003300^3 = 兩千七百萬,勉強過啦…

class Solution {
    public int countTriplets(int[] arr) {
        int cnt = 0, N = arr.length;
        for (int i = 0; i < N-1; i++) {
            int a = arr[i];
            for (int j = i+1; j < N; j++) {
                int b = arr[j];
                for (int k = j; k < N; k++) {
                    if (k != j) b ^= arr[k];
                    if (a == b) cnt++;
                }
                a ^= arr[j];
            }
        }
        return cnt;
    }
}

前綴和

較容易理解的算法,預處理好每一段的異或和,

class Solution {
    public int countTriplets(int[] arr) {
        int cnt = 0, N = arr.length, pre[] = new int[N+1];
        for (int i = 1; i <= N; i++) pre[i] = pre[i-1] ^ arr[i-1];

        for (int i = 1; i <= N-1; i++)
        for (int j = i+1; j <= N; j++)
        for (int k = j; k <= N; k++) {
            if ((pre[i-1] ^ pre[j-1]) == (pre[j-1] ^ pre[k]))
                cnt++;
        }
        return cnt;
    }
}

複雜度分析

  • 時間複雜度:O(n3)O(n^3)
  • 空間複雜度:O(1)O(1)

不知道還有沒有更優的解法…


方法三:最優解

  • 由位運算規則得:a^b=0,得 a=ba=b
  • 如果 a = b,即 arr[i] ^ … ^ arr[k] = 0,那麼 j 只要在 [i+1, k] 中選一個數即可,爲什麼?
  • 由上可得,如果 a[i] ^ … ^ a[j-1] = C ,那麼 a[j] ^… a[k] 的值也必須等於 C,否則,兩者異或的值就不等於 0
class Solution {
    public int countTriplets(int[] arr) {
        int n = arr.length, cnt = 0;
        for (int i = 0; i < n; i++) {
            int a = arr[i];
            for (int j = i + 1; j < n; j++) {
                a ^= arr[j];
                if (a == 0)
                    cnt += j - i;
            }
        } 
        return cnt;
    }
}

複雜度分析

  • 時間複雜度:O(n2)O(n^2)
  • 空間複雜度:O(1)O(1)

還有更優的做法嗎?


方法四:前綴和 + map

佛樂…

代辦...

複雜度分析

  • 時間複雜度:O(n)O(n)
  • 空間複雜度:O(n)O(n)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章