A brute force approach, by recursing and treating the three parts each "bracket" splits the input into as separate inputs (since they can never interact).
A lot of typical optimizations could be possible, such as memoizing on range, or memoizing on pattern (and compressing the pattern to 2 characters per byte for the hash), but this might be slower.
However, a better optimization would be based on some topological insight, for example that we don't need to ever pick a smaller bracket if that doesn't release a different (or keep track of necessary?) letter to the outside.
(eg. AXABYYB doesn't need to be aXabYyb, only aXabyYb, AXABYXYB doesn't need to be aXabYxyb, only aXabyxYb (yx can now match inside, in mid part))
Or some other observation of the sort.
#![feature(format_args_capture)]fndig(r:&[u8])->Result<usize,()>{letmutnum=0;// take one letter at a timefor(left,l)inr.iter().enumerate(){// recurse on leftletleft_max=dig(&r[..left])?;letrest=&r[left+1..];// get matching letterletmatched=matchl{b'A'=>b'B',b'B'=>b'A',b'X'=>b'Y',b'Y'=>b'X',_=>returnErr(()),};letmutmid_max=usize::MAX;// get indexes of matching letter in `rest`forrightinrest.iter().enumerate().rev().filter_map(|(i,&x)|(x==matched).then(||i)){// if it was previously zero, it's still zero after shrinking// honestly a questionable optimization, since it can cause branchingmid_max=ifmid_max!=0{dig(&rest[..right])?}else{0};letend_max=dig(&rest[right+1..])?;letnum2=1+left_max+mid_max+end_max;ifnum2>num{num=num2;}}}Ok(num)}fnmain(){forsin&["AAXXXBBYYY","BXABAYBA","AAAAAAAA","ABABABAB","BABBYYAYAAB","error",]{println!("{s} -> {:?}",dig(s.as_bytes()));}}
Rust
A brute force approach, by recursing and treating the three parts each "bracket" splits the input into as separate inputs (since they can never interact).
A lot of typical optimizations could be possible, such as memoizing on range, or memoizing on pattern (and compressing the pattern to 2 characters per byte for the hash), but this might be slower.
However, a better optimization would be based on some topological insight, for example that we don't need to ever pick a smaller bracket if that doesn't release a different (or keep track of necessary?) letter to the outside.
(eg.
AXABYYB
doesn't need to beaXabYyb
, onlyaXabyYb
,AXABYXYB
doesn't need to beaXabYxyb
, onlyaXabyxYb
(yx
can now match inside, inmid
part))Or some other observation of the sort.
Look at it go!
Version history:
Ver 1
Iterator::max
for inner iteratorInitial