lovelyly
Senior Member
Có tính chất này à, ảo vậyBecause now two adjacent elements are not equal so they would differ in atleast 1 bit making that bit 0, so in atmax 31 moves, the AND becomes 0 and then will remain 0.

Có tính chất này à, ảo vậyBecause now two adjacent elements are not equal so they would differ in atleast 1 bit making that bit 0, so in atmax 31 moves, the AND becomes 0 and then will remain 0.

Cái proof đó nghe hơi ảo. Có vẻ k đc chuẩn lắm. Hoặc mình đang hiểu sai.Có tính chất này à, ảo vậy![]()
Cái "atmax 31 moves, the AND becomes 0 and then will remain 0" em nghĩ ko đúng, ví dụ như mảng con "6,5,6,5..." thì sau 31 lần, AND vẫn ra 4 chứWe know that AND operation is non-increasing in nature(constant or decreasing). so we know that
adjacent equal elements will lead to AND being constant so first just remove all equal adjacent elements and then
for each element start taking AND within a loop that will run just 31 times for each element.
Because now two adjacent elements are not equal so they would differ in atleast 1 bit making that bit 0, so in atmax
31 moves, the AND becomes 0 and then will remain 0.
So for each element, keep on performing the loop till i + 31, and stop if curr_and <= k or curr_and = 0.
TC: O(UNQ*31) where UNQ = number of distinct elements.
Vẫn còn non thật, quá đơn giản mà ko nghĩ ra
.class Solution {
int ansFromTo(int from, int to, int[][] count) {
int[] res = new int[30];
for (int j = 0; j < 30; j++) {
res[j] = count[to][j] - (from == 0 ? 0 : count[from-1][j]);
}
// System.out.println("from: " + from + " to: " + to + " res: " + Arrays.toString(res));
int ans = 0;
for (int j = 29; j >= 0; j--) {
ans = ans << 1;
int bit = res[j] > 0 ? 0 : 1;
ans = ans | bit;
// ans += bit * Math.pow(2, j);
}
return ans;
}
public int minimumDifference(int[] nums, int k) {
int n = nums.length;
int[][] count = new int[n][30];
for (int i = 0; i < n; i++) {
int num = nums[i];
int j = 0;
while (j < 30) {
int prev = (i == 0) ? 0 : count[i-1][j];
count[i][j] = prev + ((num % 2 == 0) ? 1 : 0);
num = num/2;
j++;
}
}
// System.out.println(Arrays.deepToString(count));
int ans = Integer.MAX_VALUE;
for (int i = 0; i < n; i++) {
// System.out.println("i: " + i);
int l = -1;
int r = i+1;
while (r - l > 1) {
int m = (l+r)/2;
int val = ansFromTo(m, i, count) - k;
// System.out.println("l: "+ l + " r: " + r + " m: " + m + " i: " + i + " is: " + ansFromTo(m, i, count));
ans = Math.min(Math.abs(val), ans);
if (val < 0) {
l = m;
}
else if (val > 0) {
r = m;
}
else {
break;
}
}
if (ans == 0) {
break;
}
}
return ans;
}
}
Ừ mình đọc kĩ lại thì có vẻ ko đúng.Cái "atmax 31 moves, the AND becomes 0 and then will remain 0" em nghĩ ko đúng, ví dụ như mảng con "6,5,6,5..." thì sau 31 lần, AND vẫn ra 4 chứ
Sau 1 hồi suy nghĩ thì cũng ra được cái proof cho cách giải này.Bài 4 t dùng set để tính prefix and. Hết contest mới làm xong,
Python:class Solution: def minimumDifference(self, nums: List[int], k: int) -> int: ret = abs(nums[0] - k) pre = {nums[0]} for x in nums: next_set = {x} ret = min(ret, abs(x - k)) for y in pre: next_set.add(y & x) ret = min(ret, abs((y & x) - k)) pre = next_set return ret
O(n * len(pre)). Mấu chốt ở đây là cần tính được cái size của pre trong worse case.nums = x0, x1, x2, x3, .., xnpre tại vị trí k = set(pre0, pre1, pre2,..., prek)
với:
pre0 = x0 & x1 & x2 & ... & xk
pre1 = x1 & x2 & ... & xk
pre2 = x2 &... & xk
...
prek = xk
suy ra:
pre0 = x0 & pre1
pre1 = x1 & pre2
pre2 = x2 & pre3
....
len(set(pre0,pre1,pre2,....,pre3)) lớn nhất đó là sau mỗi lần & thì chỉ mất đi 1 bit 1. Mà 1 <= nums[i] <= 10^9 nên suy ra cái len(set(pre0,pre1,pre2,....,pre3)) = 31 trong trường hợp worse case.cứ cắm đầu giải leetcode ko lo cập nhập kiến thức công nghệ thì chả lởmSao dạo này mấy ông dev Leetcode lởm vãi. Lần trước tầm 10p cuối đã lag vãi trưởng rồi. Lần này sập luôn![]()

class Solution:
def minimumDifference(self, nums: List[int], k: int) -> int:
n = len(nums)
pref = []
curr = [0 for i in range(32)]
for num in nums:
b = bin(num)[2:]
for i, j in enumerate(b[::-1]):
curr[i] += 1 if j == '1' else 0
pref.append([i for i in curr])
pref.append([0 for i in range(32)])
res = abs(nums[0] - k)
@cache
def calcAND(start, en):
res = 0
f1 = pref[start]
f2 = pref[en]
for i in range(32):
if f2[i] - f1[i] == en - start:
res += 2 ** i
return res
for i in range(n):
res = min(abs(nums[i] - k), res)
if nums[i] < k:
continue
if nums[i] == k:
return 0
l, r = i - 1, n - 1
while l <= r:
mid = (l + r) // 2
v = calcAND(i - 1, mid)
res = min(abs(v - k), res)
if v < k:
r = mid - 1
elif v > k:
l = mid + 1
else:
return 0
return res
Cái này hóng các cao nhân trong topic thôiWeekly Contest Q4. https://leetcode.com/problems/find-subarray-with-bitwise-and-closest-to-k/description/
Mình có giải theo hướng như sau:
Python:class Solution: def minimumDifference(self, nums: List[int], k: int) -> int: n = len(nums) pref = [] curr = [0 for i in range(32)] for num in nums: b = bin(num)[2:] for i, j in enumerate(b[::-1]): curr[i] += 1 if j == '1' else 0 pref.append([i for i in curr]) pref.append([0 for i in range(32)]) res = abs(nums[0] - k) @cache def calcAND(start, en): res = 0 f1 = pref[start] f2 = pref[en] for i in range(32): if f2[i] - f1[i] == en - start: res += 2 ** i return res for i in range(n): res = min(abs(nums[i] - k), res) if nums[i] < k: continue if nums[i] == k: return 0 l, r = i - 1, n - 1 while l <= r: mid = (l + r) // 2 v = calcAND(i - 1, mid) res = min(abs(v - k), res) if v < k: r = mid - 1 elif v > k: l = mid + 1 else: return 0 return res
Ý tưởng là lưu prefix set bit của dãy để tính toán nhanh AND của subset, sau đó duyệt i từ 0 - n-1, với mỗi i dùng binary search tìm giá trị gần k nhất do giá trị AND của các subset bắt đầu từ phần tử i là dãy giảm dần.
TC của thuật toán là O(nlog(n)), tuy nhiên không hiểu sao submit vẫn bị TLE. Không biết TC của mình ước lượng có bị sai không ạ?
leetcode time complexity ảo quá. Em pass được 809/816 test, chạy test cuối mất 8800ms trong khi lời giải thấy chỉ O(nlog(n)), limit cũng chỉ 10^5 mà taCái này hóng các cao nhân trong topic thôileetcode time complexity ảo quá.
Hình như bài 4 bữa trước chỉ có mỗi 1 fence trong topic giải được còn lại rụng hết.
via theNEXTvoz for iPhone
Bác thử dùng dịch bit thay vì dùng lũy thừa xem ổn hơn không, đoạn này:Weekly Contest Q4. https://leetcode.com/problems/find-subarray-with-bitwise-and-closest-to-k/description/
Mình có giải theo hướng như sau:
Python:class Solution: def minimumDifference(self, nums: List[int], k: int) -> int: n = len(nums) pref = [] curr = [0 for i in range(32)] for num in nums: b = bin(num)[2:] for i, j in enumerate(b[::-1]): curr[i] += 1 if j == '1' else 0 pref.append([i for i in curr]) pref.append([0 for i in range(32)]) res = abs(nums[0] - k) @cache def calcAND(start, en): res = 0 f1 = pref[start] f2 = pref[en] for i in range(32): if f2[i] - f1[i] == en - start: res += 2 ** i return res for i in range(n): res = min(abs(nums[i] - k), res) if nums[i] < k: continue if nums[i] == k: return 0 l, r = i - 1, n - 1 while l <= r: mid = (l + r) // 2 v = calcAND(i - 1, mid) res = min(abs(v - k), res) if v < k: r = mid - 1 elif v > k: l = mid + 1 else: return 0 return res
Ý tưởng là lưu prefix set bit của dãy để tính toán nhanh AND của subset, sau đó duyệt i từ 0 - n-1, với mỗi i dùng binary search tìm giá trị gần k nhất do giá trị AND của các subset bắt đầu từ phần tử i là dãy giảm dần.
TC của thuật toán là O(nlog(n)), tuy nhiên không hiểu sao submit vẫn bị TLE. Không biết TC của mình ước lượng có bị sai không ạ?
2 ** i

Đúng là vấn đề nằm ở hàm Calc này thật, do O(32*nlogn) chạy lâu thật bác ạBác thử dùng dịch bit thay vì dùng lũy thừa xem ổn hơn không, đoạn này:
Nguyên nhân TLE có thể do O(nlogn) nhưng hằng số lại lớn (32*nlogn), nên có thể phải tối ưu từng phần nhỏ.Python:2 ** i
) Cơ mà không biết tối ưu làm sao phần này nữaMình cũng không rõ lắm về Python. Cách của mình giống cách của bạn, nếu mình dùng lũy thừa thì cũng chỉ pass 810 cases.Đúng là vấn đề nằm ở hàm Calc này thật, do O(32*nlogn) chạy lâu thật bác ạ) Cơ mà không biết tối ưu làm sao phần này nữa
int ansFromTo(int from, int to, int[][] count) {
int[] res = new int[30];
for (int j = 0; j < 30; j++) {
res[j] = count[to][j] - (from == 0 ? 0 : count[from - 1][j]);
}
// System.out.println("from: " + from + " to: " + to + " res: " + Arrays.toString(res));
int ans = 0;
for (int j = 29; j >= 0; j--) {
ans = ans << 1;
int bit = res[j] > 0 ? 0 : 1;
ans = ans | bit;
// ans += bit * Math.pow(2, j);
}
return ans;
}
Mình cũng không rõ lắm về Python. Cách của mình giống cách của bạn, nếu mình dùng lũy thừa thì cũng chỉ pass 810 cases.
Mã:int ansFromTo(int from, int to, int[][] count) { int[] res = new int[30]; for (int j = 0; j < 30; j++) { res[j] = count[to][j] - (from == 0 ? 0 : count[from - 1][j]); } // System.out.println("from: " + from + " to: " + to + " res: " + Arrays.toString(res)); int ans = 0; for (int j = 29; j >= 0; j--) { ans = ans << 1; int bit = res[j] > 0 ? 0 : 1; ans = ans | bit; // ans += bit * Math.pow(2, j); } return ans; }
class Solution:
def minimumDifference(self, nums: List[int], k: int) -> int:
res = float('inf')
start = 0
count_bit = defaultdict(int)
def get_curr_and(end):
length = end - start + 1
res = 0
for bit_index in range(32):
bit_val = 1 << bit_index
if count_bit[bit_index] == length:
res |= bit_val
return res
def add_num(num):
nonlocal count_bit
for bit_index in range(32):
bit_val = 1 << bit_index
if bit_val & num:
count_bit[bit_index] += 1
def remove_num(num):
nonlocal count_bit
for bit_index in range(32):
bit_val = 1 << bit_index
if bit_val & num:
count_bit[bit_index] -= 1
for end, num in enumerate(nums):
add_num(num)
while start <= end and get_curr_and(end) < k:
res = min(k - get_curr_and(end), res)
remove_num(nums[start])
start += 1
res = min(abs(k - get_curr_and(end)), res)
return res
Đệt do phần này hả em, anh cũng pass hết còn 10 test cases mà cũng xài hàm pow. Fak.Đúng là vấn đề nằm ở hàm Calc này thật, do O(32*nlogn) chạy lâu thật bác ạ) Cơ mà không biết tối ưu làm sao phần này nữa

Mình dùng Java thì pass. Bỏ Python đi bác :v.Xem tệp đính kèm 2528265
Mình cũng giống bác, vậy bài này phải tối ưu hơn 32nlog(n) thì mới pass được rồi