Last active
October 9, 2024 03:56
-
-
Save robert-king/f23f93b8895b904aeac534970314fc5e to your computer and use it in GitHub Desktop.
meta hacker cup 2024, Problem E: Wildcard Submissions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Problem E: Wildcard Submissions | |
https://www.facebook.com/codingcompetitions/hacker-cup/2024/round-1/problems/E | |
I didn't participate but I coded up the solution to E after reading over the contest analysis | |
*/ | |
use rayon::prelude::*; | |
use std::io::{stdin, BufRead}; | |
const MODULO: u64 = 998_244_353; | |
pub fn solve_e() { | |
let mut it = stdin().lock().lines().map(|line| line.unwrap()); | |
let t: usize = it.next().unwrap().parse().unwrap(); | |
let mut test_cases = vec![]; | |
for case in 1..=t { | |
let n: usize = it.next().unwrap().parse().unwrap(); | |
let mut words = vec![]; | |
for _ in 0..n { | |
words.push(it.next().unwrap()); | |
} | |
test_cases.push((case, words)); | |
} | |
let lines: Vec<String> = test_cases | |
.par_iter() | |
.map(|(case, words)| { | |
let ans = solve(words); | |
eprintln!("Case #{case}: {ans}"); | |
format!("Case #{case}: {ans}") | |
}) | |
.collect(); | |
println!("{}", lines.join("\n")); | |
} | |
fn solve(s: &Vec<String>) -> u64 { | |
let lcp = get_lcp(s); | |
let v = get_v(s); | |
let n = s.len(); | |
let mut odd = 0; | |
let mut even = 0; | |
for s in 1..1 << n { | |
let mut count = 0; | |
for j in (0..lcp[s]).rev() { | |
count += 1; | |
if (v[s] >> j) & 1 == 1 { | |
count *= 26; | |
} | |
count %= MODULO; | |
} | |
if s.count_ones() % 2 == 0 { | |
even += count; | |
even %= MODULO; | |
} else { | |
odd += count; | |
odd %= MODULO; | |
} | |
} | |
(1 + odd + MODULO - even) % MODULO | |
} | |
fn get_v(s: &Vec<String>) -> Vec<u128> { | |
let n = s.len(); | |
let mut v = vec![0; 1 << n]; | |
for i in 0..n { | |
for (j, c) in s[i].chars().enumerate() { | |
if c == '?' { | |
v[1 << i] |= 1 << j; | |
} | |
} | |
} | |
for s in 1usize..1 << n { | |
if s.count_ones() > 1 { | |
for i in 0..n { | |
if (s >> i) & 1 == 1 { | |
v[s] = v[s - (1 << i)] & v[1 << i]; | |
break; | |
} | |
} | |
} | |
} | |
v | |
} | |
fn get_lcp(s: &Vec<String>) -> Vec<usize> { | |
let n = s.len(); | |
let mut lcp = vec![0; 1 << n]; | |
lcp[0] = 1000; | |
for i in 0..n { | |
lcp[1 << i] = s[i].len(); | |
for j in 0..i { | |
let common_count = s[i] | |
.chars() | |
.zip(s[j].chars()) | |
.take_while(|&(x, y)| x == '?' || y == '?' || x == y) | |
.count(); | |
lcp[(1 << i) | (1 << j)] = common_count; | |
} | |
} | |
for s in 1..1 << n { | |
for i in 0..n { | |
if (s >> i) & 1 == 1 { | |
for j in i + 1..n { | |
if (s >> j) & 1 == 1 { | |
// todo: surely we don't need both i & j here. theres a simpler way | |
lcp[s] = lcp[s - (1 << i)] | |
.min(lcp[s - (1 << j)]) | |
.min(lcp[(1 << i) | (1 << j)]); | |
} | |
} | |
} | |
} | |
} | |
lcp | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment