help me uncover a conspiracy and find the assassinations foretold in moby-dick. this may be done in python, typescript, rust, or zig.
this problem briefly became popular on Esolangs in 2020 when cyanidesDuality happened across a page
satirizing the supposed "bible code", a concept popularized by Michael Drosnin's book The Bible Code, which suggests that
the Torah contains encoded predictions of significant events (written by aliens or something?). this lead to myself and umnikos engaging in a duel in which
we tried to write as efficient a program as possible to count these strings.
anyway, the basic concept behind these codes can in a few words be described by the term used by bible code enthusiasts: "equidistant letter sequence". one simply
chooses a string that they wish to claim was encoded by the bible or moby-dick or whatever, and looks for a place in the book where the chosen string appears as a
subsequence with a fixed gap between the characters.
for example, I've just written a book. look out for example book by Christina Hanson in your local bookstore. here is the text of the book:
THISBOOKISSOFUCKINGAWESOMEANDIAMTOTALLYGAY
there are all sorts of hidden codes in this message. for example, it hints that I am SAD:
THISBOOKISSOFUCKINGAWESOMEANDIAMTOTALLYGAY
and that the book is a SCAM:
THISBOOKISSOFUCKINGAWESOMEANDIAMTOTALLYGAY
it even implies I might be GAY:
THISBOOKISSOFUCKINGAWESOMEANDIAMTOTALLYGAY
however, I am not SICK. (I am in good health, in fact.) it appears as a subsequence, but the gaps between letters are inconsistent:
THISBOOKISSOFUCKINGAWESOMEANDIAMTOTALLYGAY
your challenge, given a needle and a haystack, is to find a place where the needle appears in the haystack as an equidistant subsequence,
returning both the index and the magnitude of the gap between the letters. if there is no valid result, return null (or equivalent).
not just any language is allowed, so there are fixed APIs. the first element of each result tuple is the position, and the second is the gap.
deffeelma(deez:str,nuts:str)->tuple([int,int])orNone:candice=[]ligma=len(nuts)sugondese=len(deez)forjoeinrange(sugondese):formamainrange(1,sugondese):try:doomah=""forkenyainrange(ligma):doomah=doomah+deez[joe+mama*kenya]ifdoomah==nuts:return(joe,mama)except:pass# deez nuts into your facereturnNonedefentry(haystack:str,needle:str)->tuple([int,int])orNone:returnfeelma(haystack,needle)
#include<stdbool.h>#include<stddef.h>boolentry_impl(// strings don't need to be \0 terminated because fuck thatconstchar*haystack,constsize_ts_haystack,constchar*needle,constsize_ts_needle,// outputsize_t*start,size_t*gap// thigh gap uwu){// search for first charfor(*start=0;*start<=s_haystack-s_needle;haystack++,(*start)++){// skip over chars that dont matchif(*haystack!=*needle)continue;// reset char gap*gap=0;// if there's only one char in needle, quit earlyif(s_needle==1)returntrue;// max pos for second char so the entire needle still fitsconstchar*h_max=haystack+(s_haystack-*start-1)/(s_needle-1);constchar*h=haystack+1;constchar*n=needle+1;// search for second charfor(;*n!=*h;h++,(*gap)++){// skip if no matching second char foundif(h>h_max)gotoskip;}// match remaining charsfor(size_ti=2;i<s_needle;i++){// skip if mismatchif(*++n!=*(h+=*gap+1))gotoskip;}returntrue;skip:continue;}returnfalse;}
// fuck your language limitations// just require an ABI or a I/O interface instead of being cringe#[link(name = "impl")]extern"C"{pubfnentry_impl(haystack:*constu8,s_haystack:usize,needle:*constu8,s_needle:usize,start:*mutusize,gap:*mutusize,)->bool;}pubfnentry(haystack:&str,needle:&str)->Option<(usize,usize)>{letmutstart=0;letmutgap=0;letsucc=unsafe{entry_impl(haystack.as_ptr(),haystack.len(),needle.as_ptr(),needle.len(),&mutstartas*mutusize,&mutgapas*mutusize,)};ifsucc{Some((start,gap))}else{None}}#[cfg(test)]modtests{usesuper::*;#[test]fnsad_succeeds(){assert_eq!(entry("THISBOOKISSOFUCKINGAWESOMEANDIAMTOTALLYGAY","SAD"),Some((10,8)));}#[test]fnscam_succeeds(){assert_eq!(entry("THISBOOKISSOFUCKINGAWESOMEANDIAMTOTALLYGAY","SCAM"),Some((9,4)));}#[test]fngay_succeeds(){assert_eq!(entry("THISBOOKISSOFUCKINGAWESOMEANDIAMTOTALLYGAY","GAY"),Some((39,0)));}#[test]fnsick_fails(){assert_eq!(entry("THISBOOKISSOFUCKINGAWESOMEANDIAMTOTALLYGAY","SICK"),None);}}
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "cc"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"libc",
]
[[package]]
name = "cg48"
version = "0.1.0"
dependencies = [
"cc",
]
[[package]]
name = "libc"
version = "0.2.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
exportfunctionentry(h:string,n:string):[number,number]|null{for(letg=1;n.length*g<=h.length;++g){constm=RegExp(n.split('').map(c=>(/[.*+?^${}()|[\]\\]/.test(c)?'\\':'')+c).join(`.{${g-1}}`)).exec(h);if(m)return[m.index,--g]}returnnull}// dis onli wroks on WIDE CHARS because YES
defentry(h,n):importre;r="|".join((i*".").join([f"({n[0]})",*n[1:]])foriinrange(len(h)));s=re.search(r,h);returnsand(s.start(),s.groups().index(n[0]))#nested loop is shorter#like and comment below with your favorite esolang #long live regis regex#dont' forget to ring that [shuffle] button!
Let's assume the needle of length m is in the haystack of length n. Then there exist an offset o and a gap g such that the needle can be found there. We have g < n/m (since otherwise the needle wouldn't fit). For the offset we then have o < n - gm . Let's pick g around 1/2 n/m. Then we get o < n - 1/2 n = 1/2 n. So we can get o in O(n) and at the same time g in O(n/m).
Now you've got this beautiful infinite Cantor pairing iterator. How long does it take until we reach the tuple (o, g)? Well, it basically visits the plane in a triangular zig-zag pattern; reaching (o, g) thus takes O(max(o, g)²) = O(n²) many steps.
But what happens in each step? Well, I'd hope the skip is O(1), at least for a string buffer backed iterator. The rest is however clearly O(m). So I believe we obtain O(n²m), which is not even bad.
Nice use of iterators!
structIterNatPair{next:(usize,usize),}implIterNatPair{pubfnnew()->Self{Self{next:(0,0)}}}implIteratorforIterNatPair{typeItem=(usize,usize);fnnext(&mutself)->Option<Self::Item>{letnext=self.next;self.next=matchself.next.0{0=>(self.next.1+1,0),_=>(self.next.0-1,self.next.1+1),};Some(next)}}// TODO: If needle isn't in haystack, return None. Currently, we get stuck.// I think this is hard to do without breaking infinite haystacks.// I'd be interested what time complexity this is, and how you worked it out.pubfndivine_intellect<T:PartialEq,I:Iterator<Item=T>+Clone>(haystack:I,needle:I,)->implIterator<Item=(usize,usize)>{IterNatPair::new().filter(|(_,step)|*step!=0).filter(move|(start,step)|{haystack.clone().skip(*start).step_by(*step).take(needle.clone().count()).eq(needle.clone())})}pubfnentry(haystack:&str,needle:&str)->Option<(usize,usize)>{divine_intellect(haystack.chars(),needle.chars()).next()}#[cfg(test)]modtests{usesuper::*;#[test]fniternatpair(){assert_eq!(IterNatPair::new().take(6).collect::<Vec<_>>(),[(0,0),(1,0),(0,1),(2,0),(1,1),(0,2)],);}#[test]fnshort(){assert_eq!(entry("hello","hello"),Some((0,1)));assert_eq!(entry(" hello","hello"),Some((1,1)));assert_eq!(entry("h e l l o","hello"),Some((0,2)));assert_eq!(entry(" h e l l o","hello"),Some((1,2)));assert!(matchentry("abc","b"){Some((start,_))=>start==1,None=>false,});assert!(matchentry("abc",""){Some((start,_))=>start<=3,None=>false,});matches!(entry("",""),Some((0,_)));}#[test]fnwrong_path(){assert_eq!(entry("ehello","hello"),Some((1,1)));assert_eq!(entry("hhello","hello"),Some((1,1)));assert_eq!(entry("he hello","hello"),Some((3,1)));}#[test]fnfailure(){assert_eq!(entry("hello","hi"),None);}#[test]fnlong(){assert_eq!(entry("abhegggggggggaachjkhfasd h e l l o a","hello"),Some((25,2)),);}}
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "by-the-gods"
version = "0.1.0"
post a comment