# 22.11.2024 [1072. Flip Columns For Maximum Number of Equal Rows]
Max same-bit-rows after flipping columns in 01-2D matrix #medium #matrix
22.11.2024
1072. Flip Columns For Maximum Number of Equal Rows medium blog post substack youtube deep-dive
Join me on Telegram
Problem TLDR
Max same-bit-rows after flipping columns in 01-2D matrix #medium #matrix
Intuition
Let's observe what's happening:
// 0 0 0 ---
// 0 0 1 ff- or --f <-- mask
// 1 1 0 ff- or --f
// 1 1 1 ---
// v
// 0 0 1
// 0 0 0
// 1 1 1
// 1 1 0
// *
//
// 0 1 0
// 0 1 1
// 1 0 0
// 1 0 1
// * <-- intermediate column flips are irrelevant
// 0 0 0 0 0 *
// 1 1 1 1 1 *
// 0 0 0 0 1 *
// 1 1 1 1 0 *
// 0 0 0 1 1 * <-- symmetry
// 1 1 1 0 0 * <-- symmetry
// 0 0 1 1 1 *
// 1 1 0 0 0 *
// 0 1 1 1 1 *
// 1 0 0 0 0 *
Some observations:
intermediate flips are irrelevant, only pattern-flips can improve the situation
each row has a pattern and this pattern has a symmetry with its inverted version
the pattern and its inversion forms a groups, group size is the answer
Approach
one trick to collapse pattern with its inversion is to xor each with the first bit (@lee's brain idea)
in Kotlin, simple groupBy works faster than strings hashes
in Rust [u8] key is faster than
[u128, u128, u128], or[u64; 5]keysc++ has bitset built-in
Complexity
Time complexity: $$O(nm)$$
Space complexity: $$O(nm)$$
Code
fun maxEqualRowsAfterFlips(matrix: Array<IntArray>) =
matrix.groupBy { r -> r.map { it xor r[0] }}
.maxOf { it.value.size }
pub fn max_equal_rows_after_flips(matrix: Vec<Vec<i32>>) -> i32 {
*matrix.iter().fold(HashMap::new(), |mut hm, r| {
*hm.entry(r.iter().map(|&b| r[0] ^ b).collect::<Vec<_>>())
.or_insert(0) += 1; hm
}).values().max().unwrap() as i32
}
int maxEqualRowsAfterFlips(vector<vector<int>>& m) {
unordered_map<bitset<300>, int>c; int r = 0;
for (auto v: m) {
bitset<300>b;
for (int i = 0; i < size(v);) b[i] = v[0] ^ v[i++];
r = max(r, ++c[b]);
}
return r;
}

