19
\$\begingroup\$

Take this array:

[1, 2, 6, 4, 4, 1, 0, 0, 2, 0, 4, 1, 4, 2, 4, 8, 1, 2, 4]

There are quite a few places where [1, ..., 2, ..., 4] appears, where ... is any number of items. These include [1, 2, 6, 4], [1, 0, 0, 2, 0, 4], [1, 0, 0, 2, 0, 4, 1, 4], and [1, 2, 4]. The shortest of these is simply [1, 2, 4].

Task:

Given two arrays of any (reasonable) data type you choose, find the shortest slice of the first which contains all items in the second in order. There is guaranteed to be at least one such slice.

If there are multiple, you may return any one of them. Neither array will be empty. Duplicate items may appear in the second list.

Test cases:

[1, 2] [1, 2] -> [1, 2]
[2, 1, 3, 2] [1, 2] -> [1, 3, 2]
[1, 2, 4, 8] [1, 4, 8] -> [1, 2, 4, 8]
[0, 1, 1, 0, 0, 1, 1, 1, 0] [0, 1, 0] -> [0, 1, 1, 0]
[1, 5, 3, 7, 1, 3, 3, 5, 7] [1, 3, 5, 7] -> [1, 3, 3, 5, 7]
[1, 2, 5, 1, 3, 5] [1, 5] -> [1, 2, 5] OR [1, 3, 5]

Other:

This is , so shortest answer in bytes per language wins!

asked Jun 3, 2021 at 4:56
\$\endgroup\$
6
  • 7
    \$\begingroup\$ Does it have to be an array of integers - can we use strings or characters instead? Will they ever be negative? Can we require, for example, that they be in [0,9]? \$\endgroup\$ Commented Jun 3, 2021 at 6:38
  • 1
    \$\begingroup\$ There's strong Meta support for pxeger's proposal \$\endgroup\$ Commented Jun 3, 2021 at 11:09
  • \$\begingroup\$ @pxeger Sure, go ahead. Any reasonable type you want, sorry for not clarifying that. \$\endgroup\$ Commented Jun 3, 2021 at 16:51
  • \$\begingroup\$ Do the arguments have to be exactly in that order? Or can we swap them? \$\endgroup\$ Commented Jun 3, 2021 at 17:37
  • \$\begingroup\$ @binarycat Any order, I just mentioned them in that order since it makes it easier to describe. \$\endgroup\$ Commented Jun 3, 2021 at 17:38

17 Answers 17

7
\$\begingroup\$

Jelly, 7 bytes

ẆŒPi\ƇḢ

Try it online!

-1 byte thanks to Unrelated String

ẆŒPi\ƇḢ Main Link
 Ḣ Find the first
Ẇ slice (ordered by length)
 Ƈ where
 i the right list's index is truthy (occurs in)
 ŒP the powerset (stable ordered)

1-indexing saves me a byte, thank you Dennis!

answered Jun 3, 2021 at 4:58
\$\endgroup\$
3
  • \$\begingroup\$ -1 \$\endgroup\$ Commented Jun 3, 2021 at 5:20
  • \$\begingroup\$ @UnrelatedString Oh smart. Thanks \$\endgroup\$ Commented Jun 3, 2021 at 5:52
  • 2
    \$\begingroup\$ Relevant tip for Unrelated's golf (shameless plug) \$\endgroup\$ Commented Jun 4, 2021 at 2:10
6
\$\begingroup\$

Brachylog, 8 bytes

⟨s⊇⟩floh

Try it online!

⟨ ⟩f Find every
 s substring of the first input
 ⊇ containing the second input as a substring.
 lo Sort by length
 h and take the first.
answered Jun 3, 2021 at 5:09
\$\endgroup\$
6
\$\begingroup\$

Husk, 8 bytes

◄Lfo0ドルṖQ

Try it online!

yes another carriage in the 8-byte golflang answer train.

answered Jun 3, 2021 at 5:25
\$\endgroup\$
2
  • 1
    \$\begingroup\$ not only 8 bytes all answes have 3 votes too \$\endgroup\$ Commented Jun 3, 2021 at 5:27
  • \$\begingroup\$ sorry, (unrelated string) broke the pattern \$\endgroup\$ Commented Jun 3, 2021 at 6:53
6
\$\begingroup\$

J, 64 bytes

]{~0({.+i.@>:@g)@>@{[:(]/:(g=.{:-{.)&>)@(#~]=/:~&.>)@,@{[:<@I.=/

Try it online!

Well 8-byte folks, let's square the byte count, shall we?

I could save bytes here by just trying every possible subset, but this approach seemed more interesting, and for most inputs is likely faster.

the idea

Consider 1 3 5 7 f 1 5 3 7 1 3 3 5 7:

  • Create an equality table:

    1 0 0 0 1 0 0 0 0
    0 0 1 0 0 1 1 0 0
    0 1 0 0 0 0 0 1 0
    0 0 0 1 0 0 0 0 1
    
  • Get indexes of ones:

    ┌───┬─────┬───┬───┐
    │0 4│2 5 6│1 7│3 8│
    └───┴─────┴───┴───┘
    
  • Cartesian product:

    ┌───────┬───────┬───────┬───────┬
    │0 2 1 3│0 2 1 8│0 2 7 3│0 2 7 8│ ...etc...
    └───────┴───────┴───────┴───────┴
    
  • Filter only elements that are ordered:

    ┌───────┬───────┬───────┬───────┬───────┐
    │0 2 7 8│0 5 7 8│0 6 7 8│4 5 7 8│4 6 7 8│
    └───────┴───────┴───────┴───────┴───────┘
    
  • Sort by difference between first and last element:

    ┌───────┬───────┬───────┬───────┬───────┐
    │4 5 7 8│4 6 7 8│0 2 7 8│0 5 7 8│0 6 7 8│
    └───────┴───────┴───────┴───────┴───────┘
    
  • Take first element, and construct full range between first and last:

     4 5 6 7 8
    
  • Use those to index into the input:

    1 3 3 5 7
    
answered Jun 3, 2021 at 5:57
\$\endgroup\$
5
\$\begingroup\$

JavaScript (ES6), (削除) 89 87 (削除ここまで) 83 bytes

Saved 4 bytes thanks to @tsh

Expects (haystack)(needle).

a=>b=>a.reduce((r,_,i)=>b.every(x=>j=a.indexOf(x,j)+1,j=i)/r[j-i]?a.slice(i,j):r,a)

Try it online!

Commented

a => // a[] = haystack
b => // b[] = needle
a.reduce((r, _, i) => // for each entry at position i in a[]:
 b.every(x => // for each entry x in b[]:
 j = // update j to:
 a.indexOf(x, j) // the index of x in a[], starting at j
 + 1, // to which we add 1
 j = i // starting with j = i
 ) // end of every()
 / r[j - i] // test whether r[j - i] is defined
 ? // if successful:
 a.slice(i, j) // update r[] to the current slice of a[]
 : // else:
 r, // leave r[] unchanged
 a // start with r[] = a[]
) // end of reduce()

JavaScript (ES6), (削除) 91 (削除ここまで) 89 bytes

Saved 2 bytes thanks to @tsh

Expects (needle)(haystack).

b=>g=(a,n)=>a.some((_,i)=>`${s=a.slice(i,i+n)}`.match(`^${b.join`,(.*,)*`}$`))?s:g(a,-~n)

Try it online!

How?

We turn the needle b[] into a regular expression:

RegExp(`^${b.join`,(.*,)*`}$`)

Example:

[1, 4, 8] ~> /^1,(.*,)*4,(.*,)*8$/

We recursively look for the smallest n such that there exists a slice s[] of a[] of length n that matches this regular expression.

answered Jun 3, 2021 at 6:51
\$\endgroup\$
1
  • \$\begingroup\$ a=>b=>a.reduce((r,_,i)=>b.every(x=>j=a.indexOf(x,j)+1,j=i)/r[j-i]?a.slice(i,j):r,a) \$\endgroup\$ Commented Jun 3, 2021 at 10:07
4
\$\begingroup\$

JavaScript (ES6), 93 bytes

a=>b=>a.reduce(r=>1/r[p=b.map((m,j)=>t=m-a[i]?p[j]:j?p[j-1]:i),++i-t]?a.slice(t,i):r,a,i=p=0)

Try it online!

For inputs \$ a\left[1\dots N\right] \$ and \$ b\left[1\dots M\right] \$

Let matrix \$p_{N\times M}\$ be

$$ p_{i,j}=\begin{cases} i & b_j=a_i \land j=1 \\ p_{i-1,j-1} & b_j=a_i \land j>1 \land i>1 \\ p_{i-1,j} & b_j\ne a_i \land i > 1 \\ -\infty & \text{otherwise} \\ \end{cases} $$

the minimal length of output \$L\$ is

$$ L = \min_{i \in 1\dots N} i-p_{i,M}+1 $$

And the output is

$$ a\left[p_{i,M}\dots i\right] $$

where \$i\$ is the value for minimal \$L\$.

This solution works in \$ O\left(M^2\right) \$ time and \$ O\left(N\right) \$ extra memory.

answered Jun 3, 2021 at 5:43
\$\endgroup\$
4
\$\begingroup\$

Vyxal, 9 bytes

ÞS'ṗ0c;Þg

Try it Online!

 Þg # Shortest
ÞS # sublist
 ' ; # where
 ṗ # powerset 
 c # contains
 0 # input
answered May 4, 2023 at 22:55
\$\endgroup\$
3
\$\begingroup\$

05AB1E, 8 bytes

Œé.ΔæX.å

Try it online or verify all test cases.

Explanation:

Œ # Get all sublists of the (implicit) input-list
 é # Sort these sublists by smallest to largest length
 .Δ # Find the first sublist which is truthy for:
 æ # Get the powerset of this sublist
 I.å # And check if it contains the second input-list
 # (after which this shortest found result is output implicitly)
answered Jun 3, 2021 at 12:43
\$\endgroup\$
2
\$\begingroup\$

Python 2, 94 bytes

Using iter to check if s is a subsequence of b is a trick I've first seen on this answer.

def f(a,b,i=0):l=len(a);s=a[i%l:][:i/l];x=iter(s);return s*all(v in x for v in b)or f(a,b,i+1)

Try it online!

answered Jun 3, 2021 at 10:10
\$\endgroup\$
2
\$\begingroup\$

Charcoal, 60 bytes

≔E⌕Aθ⊟η⟦1ι⟧ζF⮌η«FζF⌕A...θ§κ1ι⊞υ⟦−Σκλλ⟧≔υζ≔⟦⟧υ»≔⌊ζε≔⊟εδI✂θδ+δ⊟ε

Try it online! Link is to verbose version of code. Explanation:

≔E⌕Aθ⊟η⟦1ι⟧ζ

Get the last element of the array to match and find all of its indices in the array to search, yielding an array [1, i] for each position. These values relate to the length and start position of a slice of the array to search, initially only containing the last element of the array to match.

F⮌η«

Loop over the remaining elements in reverse order.

Loop over the matches so far.

F⌕A...θ§κ1ι

Loop over all the positions of the next element in the prefix of the current match.

⊞υ⟦−Σκλλ⟧

Save the new match length and start position.

≔υζ≔⟦⟧υ

Move the working list back to the list of matches and clear the working list again.

»≔⌊ζε≔⊟εδ

Get the match with the shortest length and get its start position.

I✂θδ+δ⊟ε

Output the slice of that length at that position.

answered Jun 3, 2021 at 11:32
\$\endgroup\$
2
\$\begingroup\$

Retina, 34 bytes

1A`
,
.$*
~)`^
1G`¶Lw`
N$`
$.&
1G`

Try it online! Takes two newline-separated lists of distinctive words (i.e. no word is contained in another word) but link is to test suite that splits on semicolons for convenience. Explanation:

1A`

Delete the array to search.

,
.$*

Replace each comma with a regex for arbitrary content.

^
1G`¶Lw`

Prefix a command to delete the array to match and list all overlapping matches in the array to search.

~)`

Evaluate the result on the original input.

N$`
$.&

Sort the matches by length.

1G`

Take the first.

answered Jun 3, 2021 at 11:46
\$\endgroup\$
2
\$\begingroup\$

R, (削除) 95 (削除ここまで), 94 bytes

Assuming combn always returns sorted indexes

At the moment the assumption is valid in all R versions, but is not documented; the alternative (longer) solution is reported at the bottom of the answer

  • -1 byte thanks to @Dominc van Essen
function(v,s,`+`=length){for(x in combn(+v,+s,,F))if(all(v[x]==s)&+v[T]>+(y=x:x[+x]))T=y;v[T]}

Try it online!

Unrolled code with explanation:

function(v,s){ # take full vector v and the vector to search s
 r=v # init shortest slice r to full vector v
 for(x in combn(length(v), # for each combination x of length(s) elements taken 
 length(s),,F){ # from the vector of indexes 1:length(v)
 # (we assume x is sorted ascending)
 a=v[x:x[+x]] # get the slice between first and last index in x 
 if(all(v[x]==s) & # if the values of the combinations == s and
 length(r) > length(a)) # the slice is shorter than r
 r=a # store the new slice
 }
 r # return the shortest slice
}

Version without assumption:

R, 107 bytes

function(v,s,r=v,`+`=length){for(x in combn(+v,+s,,F)){y=sort(x);if(all(v[x]==s)&+r>+(a=v[x:x[+x]]))r=a};r}

Try it online!

answered Jun 4, 2021 at 12:25
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Really nice! I think you might be able to save 1 byte with a bit of rearrangement... \$\endgroup\$ Commented Jun 4, 2021 at 16:00
2
\$\begingroup\$

Japt, 9 bytes

ã æÈà deV

Try it

ã æÈà deV :Implicit input of arrays U=haystack & V=needle
ã :Sub-arrays of U
 æ :Get the first that returns true
 È :When passed through the following function
 à : Combinations
 d : Any
 eV : Equal to V
answered May 4, 2023 at 22:26
\$\endgroup\$
1
\$\begingroup\$

GForth, 241 bytes

: s 1 /string ;
: d 2drop ;
: o 2over ;
: h over >r begin o drop c@ scan 2>r s 2r> 2 pick 0= over 0= 2* or ?dup until nip 2nip 1+ swap r@ - r> -rot or ;
: f 2dup 2>r begin o o h dup r@ u< if 2rdrop 2>r else d then s dup 0= until d d 2r> 1+ ;

Try it online!

Formatted & commented version

also a bit ungolfed


: s 1 /string ;
: d 2drop ;
: o 2over ;
\ find the first match. if there is no match, return a string with the maximum length (-1)
: h ( target container -- caddr u )
 \ store the start address of the string on the return stack
 over >r
 begin
 \ remove characters from the front of the string until
 \ the string starts with the same character as
 \ the target string (or until it is empty)
 o drop c@ scan
 \ remove a character from the front of the target string
 2>r s 2r>
 \ some magic that gives -1 if the target string is empty,
 \ -2 if the container string is empty, and 0 otherwise
 2 pick 0= over 0= 2* or
 \ duplicate the top of the stack if it isn't zero
 ?dup
 \ exit the loop if the top of the stack is something other than zero
 until
 \ get rid of everything on the stack except for new address of the string
 \ if there was a match, this will be the address of the end of the match.
 nip 2nip
 \ use this and the saved start address to reconstruct the match.
 1+ swap r@ - r> -rot
 \ if we didn't get a match, use the magic from before to replace the length with -1
 or ;
: f ( target container -- caddr u )
 \ store the current shortest match on the return stack.
 \ since it is given that there will be a match in the array,
 \ we put that on the return stack first
 2dup 2>r
 begin
 \ find the first match, and compare its length to
 \ the length of the match on the return stack
 o o h dup r@ u< if
 \ if it is shorter, replace the string on the return stack with it
 2rdrop 2>r
 else
 \ otherwise, discard it
 d
 then
 \ remove a charachter from the front of the string
 s
 \ if there are no more charachters in the string, exit the loop
 dup 0=
 until
 \ get rid of everything and retrive the stored match
 d d 2r>
 \ there's an off by one error here somewhere. rather than fix it, just add one.
 1+ ;
```
answered Jun 3, 2021 at 19:26
\$\endgroup\$
1
\$\begingroup\$

R >= 4.1.0, 139 bytes

\(x,y,`~`=\(a,b)paste(a,collapse=b))(u<-combn(1:sum(x|1),2,\(z)if(grepl(y~" .* ",(v=x[z[1]:z[2]])~" "))v,F))[order((m=lengths(u))/!!m)][1]

Try it online!

A function that takes the x as the vector to look within and y as the vector to look for. Returns a length 1 list containing the shortest matching contiguous subsequence of x. TIO link replaces \ with function since TIO is running an earlier version of R.

answered Jun 3, 2021 at 20:39
\$\endgroup\$
1
\$\begingroup\$

Python 3.8, 152 bytes

The other python answer is too good - but just leaving mine here anyway

from itertools import*
f=lambda a,b:min(((Y:=c[0][0]),(Z:=c[-1][0]),a[Y:Z+1],Z-Y)[::-1]for c in combinations(enumerate(a),len(b))if[*zip(*c)][1]==b)[1]

older uncondensed version

from itertools import*
def f(a,b):
 #print(*permutations(a), sep='\n')
 combis = [*combinations(enumerate(a),len(b))]
 #print(combis)
 F=[(combi[0][0],combi[-1][0]) for combi in combis if [*zip(*combi)][1]==b]
 #print(F)
 s,e = min(F, key=lambda i:i[1]-i[0])
 #print(s,e,a,a[s:e+1])
 return a[s:e+1]
answered Jun 16, 2021 at 1:56
\$\endgroup\$
1
\$\begingroup\$

Wolfram Language (Mathematica), 67 bytes

(c=___;#1/.{c,x:Shortest[PatternSequence@@Riffle[#2,c]..],c}:>{x})&

Try it online!

No brutal force, just patterns usage

answered May 4, 2023 at 18:51
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.