This blog compiles what I have learned and my insights into solving problems. Some people might benefit from it, so enjoy!
Credit to USACO Guide for the problem list. Thank you so much!
Theory
- String hashing converts a string into a number by treating its characters like digits in a chosen base. For example,
Unable to parse markup [type=CF_MATHJAX]
$. - We can map
Unable to parse markup [type=CF_MATHJAX]
$ to
Unable to parse markup [type=CF_MATHJAX]
$ and build a number using a base
Unable to parse markup [type=CF_MATHJAX]
$. - We take the result modulo
Unable to parse markup [type=CF_MATHJAX]
$ to keep the number small. However, this creates the possibility of a collision: two different strings can have the same hash modulo
Unable to parse markup [type=CF_MATHJAX]
$.
Template
const ll Mod = 1000000009LL;
const ll Base = 271LL;
struct hashed_string{
private:
string s; ll n;
vector <ll> prefix;
vector <ll> pow;
public:
hashed_string(string a){
s = a; n = a.size();
prefix.assign(n, 0);
ll sum = 0;
pow.assign(n+1, 0);
pow[0] = 1;
for (int i = 1; i <= n; i++){
pow[i] = (pow[i-1] * Base) % Mod;
}
for (int i = 0; i < n; i++){
prefix[i] = ((sum * Base) % Mod + s[i] - 'a' + 1) % Mod;
sum = prefix[i];
}
}
ll get_hash(ll l, ll r){
ll x = prefix[r];
if (l >= 1){
l--;
x = ((x - prefix[l] * pow[r - l] % Mod) % Mod + Mod) % Mod;
}
return x;
}
};
Problem List
Problem 1
- This problem uses template hashing. The number of comparisons is approximately
Unable to parse markup [type=CF_MATHJAX]
$. - For each candidate period length
Unable to parse markup [type=CF_MATHJAX]
$, compare every complete segment
Unable to parse markup [type=CF_MATHJAX]
$ with
Unable to parse markup [type=CF_MATHJAX]
$. - If the final segment is shorter than
Unable to parse markup [type=CF_MATHJAX]
$, compare it with the prefix of the same length.
Problem 2
- This problem is interesting because deleting a phrase might create a new censored phrase.
- If we delete a segment
Unable to parse markup [type=CF_MATHJAX]
$, the remaining parts
Unable to parse markup [type=CF_MATHJAX]
$ and
Unable to parse markup [type=CF_MATHJAX]
$ join together. A new censored occurrence can cross this join. - This suggests changing the template from taking a complete string during initialization to starting with an empty string and allowing characters to be added one by one. - Add characters one by one. Whenever the current suffix equals the censored phrase, delete that suffix and continue. Maintain the prefix hashes alongside the characters so that deletions restore the previous state.
Problem 3
- This problem combines template hashing with an optimization trick.
- Let
Unable to parse markup [type=CF_MATHJAX]
$ and
Unable to parse markup [type=CF_MATHJAX]
$ be the counts of
Unable to parse markup [type=CF_MATHJAX]
$ and
Unable to parse markup [type=CF_MATHJAX]
$ in
Unable to parse markup [type=CF_MATHJAX]
$. If the replacement lengths are
Unable to parse markup [type=CF_MATHJAX]
$ and
Unable to parse markup [type=CF_MATHJAX]
$, then
Unable to parse markup [type=CF_MATHJAX]
$. - Enumerate the replacement length for the more frequent symbol. Since
Unable to parse markup [type=CF_MATHJAX]
$, there are at most
Unable to parse markup [type=CF_MATHJAX]
$ candidate lengths. - For each candidate, calculate the other length and check the pattern using hashing in
Unable to parse markup [type=CF_MATHJAX]
$. This gives
Unable to parse markup [type=CF_MATHJAX]
$ time after hash preprocessing. - Both replacement strings must be nonempty, and they must be different.
Problem 4
- Some people use hashing and consider all permutations, but I use bitmask DP; it feels simpler to me.
- First, remove any string that is a substring of another remaining string. Keep only one copy of duplicate strings.
- For every pair
Unable to parse markup [type=CF_MATHJAX]
$, calculate the maximum overlap between a suffix of string
Unable to parse markup [type=CF_MATHJAX]
$ and a prefix of string
Unable to parse markup [type=CF_MATHJAX]
$. - Then use bitmask DP. Let
Unable to parse markup [type=CF_MATHJAX]
$ represent the minimum length after combining the strings in
Unable to parse markup [type=CF_MATHJAX]
$ and ending with string
Unable to parse markup [type=CF_MATHJAX]
$. - We only need to remember the last string because we have already removed strings contained in other strings.
Problem 5
- Binary search the substring length and use an
Unable to parse markup [type=CF_MATHJAX]
$ check, giving a total complexity of
Unable to parse markup [type=CF_MATHJAX]
$. - The check uses template hashing to compare the substrings.
Problem 6
- This problem uses simulation from each starting position. Hash the turn information together with the distances, then calculate the extra distance needed.
- We need to distinguish straight movement, left turns, and right turns. In terms of directed turning angles, we can encode
Unable to parse markup [type=CF_MATHJAX]
$,
Unable to parse markup [type=CF_MATHJAX]
$, and
Unable to parse markup [type=CF_MATHJAX]
$ as three different values. - I used vectors to handle the turns. There is probably a cleaner implementation.
Problem 7
- This problem is really interesting, and the key observation is hard to find.
- If we brute force every pair of strings
Unable to parse markup [type=CF_MATHJAX]
$ and calculate their Hamming distance, the complexity is
Unable to parse markup [type=CF_MATHJAX]
$. This is too slow when
Unable to parse markup [type=CF_MATHJAX]
$ is large. - What if we choose the approach based on which dimension is smaller? If
Unable to parse markup [type=CF_MATHJAX]
$ is small, use
Unable to parse markup [type=CF_MATHJAX]
$; if
Unable to parse markup [type=CF_MATHJAX]
$ is small, use
Unable to parse markup [type=CF_MATHJAX]
$. - This gives
Unable to parse markup [type=CF_MATHJAX]
$. Since
Unable to parse markup [type=CF_MATHJAX]
$, if
Unable to parse markup [type=CF_MATHJAX]
$, the work is roughly bounded by
Unable to parse markup [type=CF_MATHJAX]
$. - If
Unable to parse markup [type=CF_MATHJAX]
$ is small, brute force as described above. - If
Unable to parse markup [type=CF_MATHJAX]
$ is small, consider every pair of positions
Unable to parse markup [type=CF_MATHJAX]
$. Delete these two positions from each string by concatenating the hashes of
Unable to parse markup [type=CF_MATHJAX]
$,
Unable to parse markup [type=CF_MATHJAX]
$, and
Unable to parse markup [type=CF_MATHJAX]
$, treating empty ranges as empty strings. - If two resulting hashes match, the original strings differ only at the deleted positions, so their Hamming distance is at most
Unable to parse markup [type=CF_MATHJAX]
$.
Problem 8
- This problem is misleading at first. The operation shifts all rows by the same amount
Unable to parse markup [type=CF_MATHJAX]
$ and all columns by the same amount
Unable to parse markup [type=CF_MATHJAX]
$. We choose one pair
Unable to parse markup [type=CF_MATHJAX]
$. - If we copy the matrix to its right, every cyclic horizontal shift becomes a contiguous window. Copying it downward does the same for vertical shifts. - The problem becomes finding the lexicographically smallest
Unable to parse markup [type=CF_MATHJAX]
$ submatrix in the doubled matrix. - Use 2D hashing: hash each row, then combine the row hashes vertically to support submatrix comparisons. - Consider each top-left position
Unable to parse markup [type=CF_MATHJAX]
$. To compare two candidates, binary search for the first differing row, then the first differing character in that row, and keep the lexicographically smaller candidate.
Problem 9
- This problem seems hard at first, but the key is
Unable to parse markup [type=CF_MATHJAX]
$, which suggests a DP with about
Unable to parse markup [type=CF_MATHJAX]
$ states. - The pieces we select from the original string do not have to be adjacent. Once we have formed a prefix of the target string, we only need to know how far that prefix extends. - Let
Unable to parse markup [type=CF_MATHJAX]
$ be the maximum target-prefix length we can form using at most
Unable to parse markup [type=CF_MATHJAX]
$ pieces from the first
Unable to parse markup [type=CF_MATHJAX]
$ characters of the original string. - We can skip the current character. Alternatively, if
Unable to parse markup [type=CF_MATHJAX]
$, find the longest common prefix of the suffixes starting at
Unable to parse markup [type=CF_MATHJAX]
$ and
Unable to parse markup [type=CF_MATHJAX]
$ using hashing and binary search. - If that common-prefix length is
Unable to parse markup [type=CF_MATHJAX]
$, update
Unable to parse markup [type=CF_MATHJAX]
$ with
Unable to parse markup [type=CF_MATHJAX]
$.
Problem 10
- This problem seems impossible at first, but the trick is clever.
- For each sequence
Unable to parse markup [type=CF_MATHJAX]
$, we can calculate its total Hamming distance to all other sequences. However, this does not prove that every distance equals
Unable to parse markup [type=CF_MATHJAX]
$: distances
Unable to parse markup [type=CF_MATHJAX]
$ and
Unable to parse markup [type=CF_MATHJAX]
$ still add up to
Unable to parse markup [type=CF_MATHJAX]
$. - The idea is to assign each sequence
Unable to parse markup [type=CF_MATHJAX]
$ a random weight
Unable to parse markup [type=CF_MATHJAX]
$. - Let
Unable to parse markup [type=CF_MATHJAX]
$ be the Hamming distance and
Unable to parse markup [type=CF_MATHJAX]
$. Check whether
Unable to parse markup [type=CF_MATHJAX]
$. - If every distance from
Unable to parse markup [type=CF_MATHJAX]
$ to another sequence is
Unable to parse markup [type=CF_MATHJAX]
$, the equality always holds. Otherwise, random weights make accidental cancellation unlikely. - We can compute the weighted distance sums efficiently by collecting the total weight for each character at each position.
Extension: XOR Equality
Theory
- Assign each distinct value
Unable to parse markup [type=CF_MATHJAX]
$ a random hash
Unable to parse markup [type=CF_MATHJAX]
$. - For a set
Unable to parse markup [type=CF_MATHJAX]
$, define
Unable to parse markup [type=CF_MATHJAX]
$. - Once these hashes are maintained, comparing
Unable to parse markup [type=CF_MATHJAX]
$ and
Unable to parse markup [type=CF_MATHJAX]
$ takes
Unable to parse markup [type=CF_MATHJAX]
$ time. This gives a randomized check for set equality. - Each distinct value must be included once. XORing every occurrence instead only tracks the parity of its frequency.
Problem 11
- To count valid subarrays
Unable to parse markup [type=CF_MATHJAX]
$, use prefix states so that the state of a subarray is determined by the states at
Unable to parse markup [type=CF_MATHJAX]
$ and
Unable to parse markup [type=CF_MATHJAX]
$. - Here, we need frequencies modulo
Unable to parse markup [type=CF_MATHJAX]
$. Ordinary XOR tracks parity, so instead hash the vector of frequencies modulo
Unable to parse markup [type=CF_MATHJAX]
$. - A subarray passes this check when
Unable to parse markup [type=CF_MATHJAX]
$, meaning that every frequency is divisible by
Unable to parse markup [type=CF_MATHJAX]
$, subject to hash collisions. - But a frequency could be
Unable to parse markup [type=CF_MATHJAX]
$, while we need every appearing value to occur exactly
Unable to parse markup [type=CF_MATHJAX]
$ times. - Maintain the minimum allowed prefix index
Unable to parse markup [type=CF_MATHJAX]
$ so that no value appears more than
Unable to parse markup [type=CF_MATHJAX]
$ times in the subarray. Combined with the modulo-
Unable to parse markup [type=CF_MATHJAX]
$ condition, this leaves only frequencies
Unable to parse markup [type=CF_MATHJAX]
$ and
Unable to parse markup [type=CF_MATHJAX]
$.
Code Thank for reading.



