problemreductions/models/set/
consecutive_sets.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
9use crate::traits::Problem;
10use serde::{Deserialize, Serialize};
11use std::collections::HashSet;
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "ConsecutiveSets",
16 display_name: "Consecutive Sets",
17 aliases: &[],
18 dimensions: &[],
19 category: crate::registry::ProblemCategory::Set,
20 module_path: module_path!(),
21 description: "Determine if a string exists where each subset's elements appear consecutively",
22 fields: &[
23 FieldInfo { name: "alphabet_size", type_name: "usize", description: "Size of the alphabet (elements are 0..alphabet_size-1)" },
24 FieldInfo { name: "subsets", type_name: "Vec<Vec<usize>>", description: "Collection of subsets of the alphabet" },
25 FieldInfo { name: "bound_k", type_name: "usize", description: "Maximum string length K" },
26 ],
27 }
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct ConsecutiveSets {
75 alphabet_size: usize,
77 subsets: Vec<Vec<usize>>,
79 bound_k: usize,
81}
82
83impl ConsecutiveSets {
84 pub fn new(alphabet_size: usize, subsets: Vec<Vec<usize>>, bound_k: usize) -> Self {
91 assert!(bound_k > 0, "bound_k must be positive, got 0");
92 let mut subsets = subsets;
93 for (i, subset) in subsets.iter_mut().enumerate() {
94 let mut seen = HashSet::with_capacity(subset.len());
95 for &elem in subset.iter() {
96 assert!(
97 elem < alphabet_size,
98 "Subset {} contains element {} which is outside alphabet of size {}",
99 i,
100 elem,
101 alphabet_size
102 );
103 assert!(
104 seen.insert(elem),
105 "Subset {} contains duplicate elements",
106 i
107 );
108 }
109 subset.sort();
110 }
111 Self {
112 alphabet_size,
113 subsets,
114 bound_k,
115 }
116 }
117
118 pub fn alphabet_size(&self) -> usize {
120 self.alphabet_size
121 }
122
123 pub fn num_subsets(&self) -> usize {
125 self.subsets.len()
126 }
127
128 pub fn bound_k(&self) -> usize {
130 self.bound_k
131 }
132
133 pub fn subsets(&self) -> &[Vec<usize>] {
135 &self.subsets
136 }
137}
138
139impl Problem for ConsecutiveSets {
140 const NAME: &'static str = "ConsecutiveSets";
141 type Solution = Vec<Option<usize>>;
142 type Value = crate::types::Or;
143
144 crate::problem_parameters![
145 ("alphabet_size", alphabet_size),
146 ("num_subsets", num_subsets),
147 ("bound_k", bound_k),
148 ];
149
150 fn evaluate(
151 &self,
152 config: &Self::Solution,
153 ) -> Result<crate::types::Or, crate::traits::EvaluationError> {
154 if config.len() != self.bound_k {
155 return Err(crate::traits::EvaluationError::InvalidConfiguration(
156 "ordering representation length does not match the bound".into(),
157 ));
158 }
159 if config
160 .iter()
161 .any(|symbol| symbol.is_some_and(|value| value >= self.alphabet_size))
162 {
163 return Err(crate::traits::EvaluationError::InvalidConfiguration(
164 "ordering representation contains an out-of-range symbol".into(),
165 ));
166 }
167 let config = config
168 .iter()
169 .map(|symbol| symbol.unwrap_or(self.alphabet_size))
170 .collect::<Vec<_>>();
171 Ok({
172 crate::types::Or({
173 let unused = self.alphabet_size;
175 let str_len = config
176 .iter()
177 .rposition(|&v| v != unused)
178 .map_or(0, |p| p + 1);
179
180 let w = &config[..str_len];
182 if w.contains(&unused) {
183 return Ok(crate::types::Or(false));
184 }
185
186 let mut subset_membership = vec![0usize; self.alphabet_size];
187 let mut seen_in_window = vec![0usize; self.alphabet_size];
188 let mut subset_stamp = 1usize;
189 let mut window_stamp = 1usize;
190
191 for subset in &self.subsets {
193 let subset_len = subset.len();
194 if subset_len == 0 {
195 continue; }
197 if subset_len > str_len {
198 return Ok(crate::types::Or(false)); }
200
201 for &elem in subset {
202 subset_membership[elem] = subset_stamp;
203 }
204
205 let mut found = false;
206 for start in 0..=(str_len - subset_len) {
207 let window = &w[start..start + subset_len];
208 let current_window_stamp = window_stamp;
209 window_stamp += 1;
210
211 if window.iter().all(|&elem| {
215 let is_member = subset_membership[elem] == subset_stamp;
216 let is_new = seen_in_window[elem] != current_window_stamp;
217 if is_member && is_new {
218 seen_in_window[elem] = current_window_stamp;
219 true
220 } else {
221 false
222 }
223 }) {
224 found = true;
226 break;
227 }
228 }
229 if !found {
230 return Ok(crate::types::Or(false));
231 }
232
233 subset_stamp += 1;
234 }
235
236 true
237 })
238 })
239 }
240
241 fn variant() -> Vec<(&'static str, &'static str)> {
242 crate::variant_params![]
243 }
244}
245
246impl crate::solvers::BruteForceProblem for ConsecutiveSets {
247 fn dimensions(&self) -> Vec<usize> {
248 vec![self.alphabet_size + 1; self.bound_k]
250 }
251}
252
253crate::declare_variants! {
254 default ConsecutiveSets => "alphabet_size^bound_k * num_subsets",
255}
256
257crate::register_brute_force! {
258 ConsecutiveSets decode |problem: &ConsecutiveSets, indices: Vec<usize>| indices.into_iter().map(|value| (value != problem.alphabet_size()).then_some(value)).collect(),
259}
260
261#[cfg(feature = "example-db")]
262pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
263 vec![crate::example_db::specs::ModelExampleSpec {
264 id: "consecutive_sets",
265 instance: Box::new(ConsecutiveSets::new(
267 6,
268 vec![vec![0, 4], vec![2, 4], vec![2, 5], vec![1, 5], vec![1, 3]],
269 6,
270 )),
271 optimal_config: serde_json::json!(vec![0, 4, 2, 5, 1, 3]),
272 optimal_value: serde_json::json!(true),
273 }]
274}
275
276#[cfg(test)]
277#[path = "../../unit_tests/models/set/consecutive_sets.rs"]
278mod tests;