#![allow(unused_macros, unused_imports, dead_code)] use std::any::TypeId; use std::cmp::{max, min, Reverse}; use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; use std::mem::swap; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign}; mod my_string { use std::ops::{Index, IndexMut}; use std::slice::SliceIndex; #[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] pub struct Str { vc: Vec, } impl Str { pub fn new() -> Self { Self { vc: vec![] } } pub fn from(s: &str) -> Self { Self { vc: s.to_string().chars().collect::>(), } } pub fn len(&self) -> usize { self.vc.len() } pub fn clear(&mut self) { self.vc.clear() } pub fn is_empty(&self) -> bool { self.vc.is_empty() } pub fn first(&self) -> Option<&char> { self.vc.first() } pub fn last(&self) -> Option<&char> { self.vc.last() } pub fn push(&mut self, c: char) { self.vc.push(c); } pub fn push_str(&mut self, s: &str) { for c in s.to_string().chars().collect::>().into_iter() { self.push(c); } } pub fn pop(&mut self) -> Option { self.vc.pop() } pub fn into_iter(self) -> std::vec::IntoIter { self.vc.into_iter() } pub fn iter(&self) -> std::slice::Iter { self.vc.iter() } pub fn iter_mut(&mut self) -> std::slice::IterMut { self.vc.iter_mut() } pub fn swap(&mut self, a: usize, b: usize) { self.vc.swap(a, b); } pub fn reverse(&mut self) { self.vc.reverse(); } pub fn find(&self, p: &Str) -> Option { let s: String = self.vc.iter().collect::(); let p: String = p.vc.iter().collect::(); s.find(&p) } pub fn rfind(&self, p: &Str) -> Option { let s: String = self.vc.iter().collect::(); let p: String = p.vc.iter().collect::(); s.rfind(&p) } pub fn into_values(self, base: char) -> Vec { self.vc .into_iter() .map(|c| (c as u8 - base as u8) as usize) .collect::>() } pub fn sort(&mut self) { self.vc.sort(); } pub fn remove(&mut self, index: usize) -> char { self.vc.remove(index) } } impl std::str::FromStr for Str { type Err = (); fn from_str(s: &str) -> Result { Ok(Str { vc: s.to_string().chars().collect::>(), }) } } impl> Index for Str { type Output = Idx::Output; fn index(&self, i: Idx) -> &Self::Output { &self.vc[i] } } impl> IndexMut for Str { fn index_mut(&mut self, index: Idx) -> &mut Self::Output { &mut self.vc[index] } } impl std::ops::Add for Str { type Output = Str; fn add(self, rhs: Self) -> Self::Output { let mut ret = self; for c in rhs.into_iter() { ret.vc.push(c); } ret } } impl std::ops::AddAssign for Str { fn add_assign(&mut self, rhs: Self) { for c in rhs.into_iter() { self.vc.push(c); } } } impl std::ops::Add for Str { type Output = Str; fn add(self, rhs: char) -> Self::Output { let mut ret = self; ret.vc.push(rhs); ret } } impl std::ops::AddAssign for Str { fn add_assign(&mut self, rhs: char) { self.vc.push(rhs); } } impl std::fmt::Display for Str { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.vc.iter().collect::()) } } impl std::fmt::Debug for Str { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.vc.iter().collect::()) } } } use my_string::Str; mod procon_reader { use std::fmt::Debug; use std::io::Read; use std::str::FromStr; pub fn read() -> T where ::Err: Debug, { let stdin = std::io::stdin(); let mut stdin_lock = stdin.lock(); let mut u8b: [u8; 1] = [0]; loop { let mut buf: Vec = Vec::with_capacity(16); loop { let res = stdin_lock.read(&mut u8b); if res.unwrap_or(0) == 0 || u8b[0] <= b' ' { break; } else { buf.push(u8b[0]); } } if !buf.is_empty() { let ret = String::from_utf8(buf).unwrap(); return ret.parse().unwrap(); } } } pub fn read_vec(n: usize) -> Vec where ::Err: Debug, { (0..n).into_iter().map(|_| read::()).collect::>() } pub fn read_vec_sub1(n: usize) -> Vec { (0..n) .into_iter() .map(|_| read::() - 1) .collect::>() } pub fn read_mat(h: usize, w: usize) -> Vec> where ::Err: Debug, { (0..h) .into_iter() .map(|_| read_vec::(w)) .collect::>>() } } use procon_reader::*; fn exit_by(msg: T) { println!("{}", msg); std::process::exit(0); } mod gcd { use std::cmp::{PartialEq, PartialOrd}; use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign}; pub fn gcd + Rem + PartialEq>(a: T, b: T) -> T { #[allow(clippy::eq_op)] let zero = a - a; if b == zero { a } else { gcd(b, a % b) } } // returns (p, q) s. t. ap + bq = gcd(a, b) pub fn ext_gcd< T: Eq + Copy + Sub + SubAssign + Mul + Div + Rem, >( a: T, b: T, ) -> (T, T) { #[allow(clippy::eq_op)] let zero = b - b; #[allow(clippy::eq_op)] let one = b / b; if a == zero { return (zero, one); } // (b % a) * x + a * y = gcd(a, b) // b % a = b - (b / a) * a // -> // (b - (b / a) * a) * x + a * y = gcd(a, b) // a * (y - (b / a) * x) + b * x = gcd(a, b) let (x, y) = ext_gcd(b % a, a); (y - b / a * x, x) } // Chinese Remainder Theorem // when exists, returns (lcm(m1, m2), x) s.t. x = r1 (mod m1) and x = r2 (mod m2) fn chinese_rem_elem2< T: Eq + Copy + Neg + PartialOrd + Add + AddAssign + Sub + SubAssign + Mul + Div + Rem + RemAssign, >( m1: T, r1: T, m2: T, r2: T, ) -> Option<(T, T)> { #[allow(clippy::eq_op)] let zero = m1 - m1; #[allow(clippy::eq_op)] let one = m1 / m1; let (p, _q) = ext_gcd(m1, m2); let g = gcd(m1, m2); if (r2 - r1) % g != zero { None } else { let lcm = m1 * (m2 / g); let mut r = r1 + m1 * ((r2 - r1) / g) * p; if r < zero { let dv = (-r + lcm - one) / lcm; r += dv * lcm; } r %= lcm; Some((lcm, r)) } } // Chinese Remainder Theorem // when exists, returns (lcm(mods), x) s.t. x = r_i (mod m_i) for all i. pub fn chinese_rem< T: Eq + Copy + Neg + PartialOrd + Add + AddAssign + Sub + SubAssign + Mul + Div + Rem + RemAssign, >( mods: &[T], rems: &[T], ) -> Option<(T, T)> { debug_assert!(mods.len() == rems.len()); #[allow(clippy::eq_op)] let zero = mods[0] - mods[0]; #[allow(clippy::eq_op)] let one = mods[0] / mods[0]; let mut lcm = one; let mut rem = zero; for (m, r) in mods.iter().copied().zip(rems.iter().copied()) { if let Some((nlcm, nrem)) = chinese_rem_elem2(lcm, rem, m, r) { lcm = nlcm; rem = nrem; } else { return None; } } Some((lcm, rem)) } } use gcd::*; mod power_with_identity { use std::ops::Mul; pub fn power_with_identity>(identity: T, base: T, mut p: usize) -> T { #[allow(clippy::eq_op)] let mut ret = identity; let mut mul = base; while p > 0 { if p & 1 != 0 { ret = ret * mul; } p >>= 1; mul = mul * mul; } ret } } use power_with_identity::power_with_identity; /************************************************************************************* *************************************************************************************/ fn main() { let solve = || { let n = read::(); let s = read::(); let t = Str::from("helloworld"); eprint!("ans = "); for i in (0..s.len()).rev() { if i + t.len() > s.len() { continue; } let mut can = true; for ti in 0..t.len() { let j = i + ti; if s[j] == '?' { continue; } if s[j] == t[ti] { continue; } can = false; break; } if can { for j in 0..n { print!("{}", if (j >= i) && (j < i + t.len()) {t[j - i] } else if s[j] == '?' {'a'} else{s[j]}); } println!(); return; } } println!("-1"); }; for _ in 0..read::() { solve(); } }