outer reuses first element of X instead of doing its job

Note this detail from the documentation for ?outer:

X and Y must be suitable arguments for FUN. Each will be extended by rep to length the products of the lengths of X and Y before FUN is called.

FUN is called with these two extended vectors as arguments (plus any arguments in ...). It must be a vectorized function (or the name of one) expecting at least two arguments and returning a value with the same length as the first (and the second).

Your version1 function is not vectorized properly like version2 is. You can see this by simply testing it on the original triples and pairs vectors, which should both match.

version1(triples, pairs)
#> [1]  TRUE FALSE
version2(triples, pairs)
#> (5, 6) (3, 5) 
#>   TRUE   TRUE

Your version1 function seems designed for use with apply(), because you retrieve a list from strsplit() but then just take the first element. If you want to maintain the approach of splitting the vector, then you would have to use the apply family of functions. Without using them, you are going to expand the triples or x vector into something much longer than y and you can't do element wise comparison.

However, I would just use something very simple. stringr::str_detect is already vectorized for string and pattern, so you can just use that directly.

library(stringr)

outer(X = triples, Y = pairs, FUN = str_detect)
#>                    (5, 6) (3, 5)
#> (1, 2)(3, 4)(5, 6)   TRUE  FALSE
#> (1, 2)(3, 5)(4, 6)  FALSE   TRUE