#3292
Hard Algorithms Minimum number of valid strings to form target ii
Array String Binary Search Dynamic Programming Segment Tree Rolling Hash String Matching Hash Function
20.4% acceptance
Feb 25, 2026
85
10
You are given an array of strings words and a string target.
A string x is called valid if x is a prefix of any string in words.
Return the minimum number of valid strings that can be concatenated to form target.
If it is not possible to form target, return -1.
Note: memory limits are smaller than usual, so you must implement a solution with linear runtime complexity.
Solution
Rust
Time O(n²)
Space O(n)
impl Solution {
pub fn min_valid_strings(words: Vec<String>, target: String) -> i32 {
let t = target.as_bytes();
let n = t.len();
// For each position i in target, compute max_match[i]:
// the longest prefix of any word that matches t[i..].
// Use Z-function on (word + '#' + target) for each word.
let mut max_match = vec![0usize; n];
for w in &words {
let wb = w.as_bytes();
let wl = wb.len();
// Build concatenated string: word + '#' + target
let mut s: Vec<u8> = Vec::with_capacity(wl + 1 + n);
s.extend_from_slice(wb);
s.push(b'#');
s.extend_from_slice(t);
let z = Self::z_function(&s);
// z[wl + 1 + i] = length of longest prefix of s that matches s[wl+1+i..]
// = length of longest prefix of word that matches t[i..], capped at wl
for i in 0..n {
let zi = z[wl + 1 + i].min(wl);
if zi > max_match[i] {
max_match[i] = zi;
}
}
}
// Greedy jump cover (Jump Game II style):
// curr_reach: rightmost position covered with `ans` valid strings
// far: rightmost position coverable by taking one more step
let mut ans = 0i32;
let mut curr_reach = 0usize;
let mut far = 0usize;
for i in 0..n {
if i > curr_reach {
return -1; // gap: cannot reach position i
}
far = far.max(i + max_match[i]);
if i == curr_reach {
if far <= curr_reach {
return -1; // cannot extend
}
curr_reach = far;
ans += 1;
if curr_reach >= n {
break;
}
}
}
if curr_reach >= n { ans } else { -1 }
}
fn z_function(s: &[u8]) -> Vec<usize> {
let n = s.len();
let mut z = vec![0usize; n];
z[0] = n;
let (mut l, mut r) = (0usize, 0usize);
for i in 1..n {
if i < r {
z[i] = (r - i).min(z[i - l]);
}
while i + z[i] < n && s[z[i]] == s[i + z[i]] {
z[i] += 1;
}
if i + z[i] > r {
l = i;
r = i + z[i];
}
}
z
}
}