Is it a circumfix?

40

4

Introduction

We all know prefixes and suffixes. But there are other types of affixes that exist too. Such as circumfixes, a type of affix that has two parts, one of which is a prefix and another of which is a suffix. Figuring out whether some string is a prefix or a suffix of some other string is easy, but what about figuring out whether it might be a circumfix?

That is today's challenge - create a program or function which takes two strings as input, and determine whether the first is a circumfix of the second.

For the purposes of this challenge a string i1 is a circumfix of another string i2 if and only if there exists some non-empty string j which is a contiguous substring of i2 such that removing j from i2 results in i1, and j is neither a prefix nor a suffix of i2 (if it is, you don't have a circumfix, you just have a suffix or a prefix respectively).

For example, "fog" is a circumfix of "frog", because removing "r" from "frog" produces "fog".

When given valid input, your program either needs to output a single consistent value of your choice if the first input string is a circumfix of the second, and any other value if it is not, or vice versa. For example, you may decide have your program output 6 when the first string is a circumfix of the second, in which case any output except 6 is acceptable when it is not.

This is , so do make sure to golf your code.

Test cases

Format:
"String 1", "String 2" -> output
    comments about the test case - in all these test cases, the output will be true if string 1 is a circumfix or string 2 and false otherwise
"apply", "appreciably" -> true
    "app]reciab[ly"
"rake", "racket by the lake" -> true
    multiple options - "r]acket by the l[ake" and "ra]cket by the la[ke"
"trout", "trumpet" -> false
    Doesn't work at all
"bring", "brought him a gong" -> false
    You only get to remove one substring - "br]ought h[i]m a go[ng" is not allowed
"falcon", "false conundrum" -> false
    You can't have extra stuff at the start or end either - "fal]se [con(undrum)" is not allowed
"goose", "goosebumps" -> false
    "goose]bumps[" is just a prefix
"lame", "blame" -> false
    And "]b[lame" is just a suffix
"pale", "pale ale" -> true
    "pale] ale[" is just a prefix, but "pal]e al[e" is a circumfix, so this is allowed
"b", "barb" -> false
    This could be a prefix ("b]arb[") or a suffix ("]bar[b"), but not a circumfix - "b]ar[b" is not allowed
"abba", "aba" -> false
    "abba" can be split into a prefix of "aba" ("ab") and a suffix of "aba" ("ba"), but "abba" is still not a circumfix of "aba"
"friend", "friend" -> false
    It's only a proper circumfix if you actually remove something - "fri][end" doesn't make the cut
"float", "on" -> false
    You may not assume the first input will be shorter than the second one
"", "" -> false
    One or both input strings may be empty
"Twin Sister", "Twister" -> false
    Inputs are ordered - you may reverse the order, but there must be a consistent ordering
"case", "Castle" -> false
    Inputs are case sensitive
"<<@ 23|>", "<<@23??|> 23|>" -> true
    "<<@]23??|>[ 23|>", not all characters will be letters)

Sara J

Posted 2019-10-26T21:13:04.767

Reputation: 2 576

Are empty inputs possible? – Luis Mendo – 2019-10-26T21:17:53.350

@LuisMendo Yes, one or both inputs may be empty – Sara J – 2019-10-26T21:19:16.400

Answers

11

J, 23 21 20 bytes

>&#*]e.1}:@}.-&#]\.[

Try it online!

-1 byte thanks to Bubbler

Return true if:

  • >&# left arg is strictly longer than right (see the "friend"/"friend" test case)
  • * and...
  • ] the right arg
  • e. is an element of the list formed by...
  • 1 }:@}. removing the first and last elements of...
  • -&# ]\. [ all the outfixes \. of the left arg [ whose size is the difference in size between the two args -&#

That is, J has a builtin to subtract the "chunks" in the middle of the necessary size and leave us with the remaining prefixes and suffixes, catted. We simply have to remove the non-proper ones (ie, the first and last elements of the list of outfixes), and check for what we're searching for.

Jonah

Posted 2019-10-26T21:13:04.767

Reputation: 8 729

+1 for the perfect use case of outfix. – Bubbler – 2019-10-28T08:58:44.943

1}:@}. in place of [:}:@}. saves a byte. Try it online! – Bubbler – 2019-10-28T09:04:47.493

@Bubbler thanks and nice catch. updated. – Jonah – 2019-10-28T12:46:53.387

8

Python3, 86 84 83 80 77 76 bytes

lambda a,b:len(a)<len(b)*any(a==b[:i]+b[i-len(a):]for i in range(1,len(a)))

Try it online!

-2 bytes thanks to @Value Ink whitespace before and after ==

-1 byte remove whitespace.

-3 bytes thanks to @ovs using any to write for loop in one line.

-3 bytes by replacing and with *

-1 byte by replacing def with lambda

Python3.8, 71 bytes

lambda a,b:(c:=len(a))<len(b)*any(a==b[:i]+b[i-c:]for i in range(1,c))

Try it online!

Angular Orbit

Posted 2019-10-26T21:13:04.767

Reputation: 81

2Welcome to CGCC! Since the goal is to have a smaller byte count, you will want to remove the whitespace in your a == b... check. – Value Ink – 2019-10-27T00:14:52.693

I put your 3.8 code into a shell and got a SyntaxError. – Alex Hall – 2019-10-27T11:35:44.713

@AlexHall fixed it. – Angular Orbit – 2019-10-27T12:01:18.373

6

Retina 0.8.2, 17 bytes

^(.+)(.+)¶\1.+\2$

Try it online! Link includes test suite that takes each test on its own line with the test values separated by a tab and converts them into individual tests with the values on separate lines as the main program expects.

Neil

Posted 2019-10-26T21:13:04.767

Reputation: 95 035

5

Jelly, 11 bytes

⁻ȧŒṖḢ;ṪƊ€iɗ

Try it online!

Takes string 1 (the potential circumfix) as the right/second argument, string 2 as the left/first argument, and outputs 0 if it's not a circumfix and a positive integer otherwise.

(The test footer is bad, but it's better than nothing.)

⁻              The arguments are not equal,
 ȧ             and
          ɗ    you also get a truthy value from the next three links:
         i     the index of the right argument (defaulting to 0) in
  ŒṖ           the list of all partitions of the left argument
        €      with each partition mapped to
    Ḣ;ṪƊ       the concatenation of its first and last elements.

Although this is mostly just a translation of my Brachylog answer, ŒṖ cannot generate empty elements of partitions while c is obligated to, so prefixes and suffixes are naturally accounted for.

This 15-byte monstrosity is what I had before I just tried putting the at the start of the program... and before I realized I could use Ḣ;Ṫ: ŒṖ1,0ịFƊ€⁸ṭṚi>1

Unrelated String

Posted 2019-10-26T21:13:04.767

Reputation: 5 300

4

Ruby, 76 62 bytes

->a,b{s=a.size;s<b.size&&(1..s-2).any?{|i|a==b[0,i]+b[i-s,s]}}

Try it online!

Value Ink

Posted 2019-10-26T21:13:04.767

Reputation: 10 608

4

Brachylog, 13 bytes

~c₃{b&}ᵐ⟨hct⟩

Try it online!

Takes string 1 (the possible circumfix) through the output variable, string 2 through the input variable, and outputs through success or failure.

I recall that at some point this could be 12 bytes, but as of now subscriptless c seems to cycle through all partitions infinitely, causing false test cases to not terminate except it would also cause false positives where the two inputs are equal.

(I tried golfing {b&}ᵐ to Xz∧X (when it's already a shorter alternative to {l>0&}ᵐ), but it has a false positive for the empty test case, since there's no problem cycling an empty list to the length of the longest when everything is empty.)

~c₃              The input variable can be split into three parts, such that
   {  }ᵐ         each of them
    b            can have its first element removed (i.e. it is not empty),
     &           and such that
        h        the first of the three
       ⟨ ct⟩     concatenated with the last of the three
                 is the output variable.

Unrelated String

Posted 2019-10-26T21:13:04.767

Reputation: 5 300

3

Zsh, 60 bytes

for ((c=#2;++i<$#1;))t=${1: i}&&2=${2:/${1%$t}?*$t}
((c-#2))

Try it online!

Key constructs here:

  • ${var:/pattern} will replace $var by the empty string if pattern matches the full string.
  • ((#var)) is zero if $var is empty.

GammaFunction

Posted 2019-10-26T21:13:04.767

Reputation: 2 838

3

Icon, 70 bytes

procedure f(a,b)
return(1<*a<*b&a==b[1:i:=2to*a]||b[i-1-*a:0]&1)|0
end

Try it online!

Galen Ivanov

Posted 2019-10-26T21:13:04.767

Reputation: 13 815

3

Bracmat, 54 bytes

(C=c w a z.!arg:(?c.?w)&@(!c:%?a (%?z&@(!w:!a % !z))))

This solution uses associative (string) pattern matching and expression evaluation during pattern matching. The associative pattern is %?a %?z, which splits the subject, !c, in two strings, neither of which is empty. (The % prefix ensures that a pattern variable does not accept a neutral element, i.e. an empty string, in the case of string pattern matching.) The expression that is evaluated during pattern matching is @(!w:!a % !z). This happens to be another associative string pattern matching operation.

Try it online!

Bart Jongejan

Posted 2019-10-26T21:13:04.767

Reputation: 71

3

Haskell, 72 71 62 bytes

\a b->or[a==take i b++drop j b|j<-[2..length b-1],i<-[1..j-1]]

Try it online!

  • -1 byte by moving l a<l b into the list comprehension, where it only needs a , rather than an &&
  • -9 bytes from benrg's suggestion

Joseph Sible-Reinstate Monica

Posted 2019-10-26T21:13:04.767

Reputation: 556

1\a b->or[a==take i b++drop j b|j<-[2..length b-1],i<-[1..j-1]] saves 9 bytes. – benrg – 2019-10-28T03:53:23.500

@benrg applied, thanks! – Joseph Sible-Reinstate Monica – 2019-10-28T12:55:36.313

3

05AB1E, 9 8 bytes

.œÅΔćìθQ

Try it online!

Outputs a non-negative number if it is a circumfix, -1 otherwise.

Grimmy

Posted 2019-10-26T21:13:04.767

Reputation: 12 521

2

JavaScript (ES6), 70 bytes

Takes input as (a)(b).

a=>b=>[...a].some((_,i)=>b[l=a.length]&&a==b.slice(0,l-i)+b.slice(-i))

Try it online!

Arnauld

Posted 2019-10-26T21:13:04.767

Reputation: 111 334

1Eventually you can avoid returning undefined moving the first test inside some – edc65 – 2019-10-28T10:47:53.860

@edc65 Good idea. Updated. – Arnauld – 2019-10-28T10:58:22.957

2

Ruby, 40 39 bytes

->a,b{a+b=~/^(.+)(.+)(?=#{b}$)\1.+\2$/}

Try it online!

How

Simple regex: if a+b can be split in 5 parts ABCDE where A==C and B==E, and b=CDE, then a is a circumfix of b.

Thanks benrg for pointing out a problem with the first solution (and saving 1 byte).

G B

Posted 2019-10-26T21:13:04.767

Reputation: 11 099

I really like this. – Jonah – 2019-10-27T18:46:25.670

2This fails on inputs like "aa","aabaa" because it finds an incorrect solution and rejects it without backtracking to look for another one. I don't know Ruby, but could you do something like /^(.+)(.+)(?=\Q$b\E$)\1.+\2$/? Then you wouldn't need the final test. – benrg – 2019-10-28T04:06:15.477

2

Python 3, 69 bytes

f=lambda c,s,i=1:i<len(c)<len(s)and(c==s[:i]+s[i-len(c):])|f(c,s,i+1)

Try it online!

-2 bytes thanks to Bubbler

Jitse

Posted 2019-10-26T21:13:04.767

Reputation: 3 566

2

JavaScript (ES6), 72 bytes

s=>t=>[...s].some((x,i)=>t[l=s.length]&&i&&s==t.slice(0,i)+t.slice(i-l))

Return true if

  • the second string is longer than the first
  • there is a position to split the first string in 2 parts, such as the second string starts with the left part and ends with the right part

Test

z=`"apply", "appreciably" -> true
    "app]reciab[ly"
"rake", "racket by the lake" -> true
    multiple options - "r]acket by the l[ake" and "ra]cket by the la[ke"
"trout", "trumpet" -> false
    Doesn't work at all
"bring", "brought him a gong" -> false
    You only get to remove one substring - "br]ought h[i]m a go[ng" is not allowed
"falcon", "false conundrum" -> false
    You can't have extra stuff at the start or end either - "fal]se [con(undrum)" is not allowed
"goose", "goosebumps" -> false
    "goose]bumps[" is just a prefix
"lame", "blame" -> false
    And "]b[lame" is just a suffix
"pale", "pale ale" -> true
    "pale] ale[" is just a prefix, but "pal]e al[e" is a circumfix, so this is allowed
"b", "barb" -> false
    This could be a prefix ("b]arb[") or a suffix ("]bar[b"), but not a circumfix - "b]ar[b" is not allowed
"abba", "aba" -> false
    "abba" can be split into a prefix of "aba" ("ab") and a suffix of "aba" ("ba"), but "abba" is still not a circumfix of "aba"
"friend", "friend" -> false
    It's only a proper circumfix if you actually remove something - "fri][end" doesn't make the cut
"float", "on" -> false
    You may not assume the first input will be shorter than the second one
"", "" -> false
    One or both input strings may be empty
"Twin Sister", "Twister" -> false
    Inputs are ordered - you may reverse the order, but there must be a consistent ordering
"case", "Castle" -> false
    Inputs are case sensitive
"<<@ 23|>", "<<@23??|> 23|>" -> true
    "<<@]23??|>[ 23|>", not all characters will be letters)`

f=s=>t=>[...s].some((x,i)=>t[l=s.length]&&i&&s==t.slice(0,i)+t.slice(i-l))

z.split('\n').forEach((s,i)=>{
    var m = s.match(/"([^"]*)", "([^"]*)" -> (true|false)/)
    if (m) {
        console.log(`"${m[1]}" "${m[2]}" should be ${m[3]} and is ${f(m[1])(m[2])}`)
    }
})

edc65

Posted 2019-10-26T21:13:04.767

Reputation: 31 086

1

Perl 5, 73 77 bytes

sub f{(grep$_[1]=~/^@{[$_[0]=~s|.{$_}|\Q$&\E.+|r]}$/,1..length($_[0])-1)?1:0}

Try it online!

Kjetil S.

Posted 2019-10-26T21:13:04.767

Reputation: 1 049

Fails on "....", "any string". Try it online!

– Value Ink – 2019-10-27T02:15:00.057

This should be one of the tests. Corrected for this now, 4 more bytes. – Kjetil S. – 2019-10-28T08:36:02.920

1

Prolog (SWI), 104 bytes

e([],_).
e([H|T],[H|U]):-e(T,U).
e(L,[_|U]):-e(L,U).
f(X,Y):-string_chars(X,A),string_chars(Y,B),e(A,B).

Try it online!

Marix

Posted 2019-10-26T21:13:04.767

Reputation: 21

1

Java (JDK), 132 bytes

a->b->{int i=0,r=0,l=a.length();for(;l<b.length()&++i<l;)r|=b.startsWith(a.substring(0,i))&b.endsWith(a.substring(i))?1:0;return r;}

Try it online!

Credits

Olivier Grégoire

Posted 2019-10-26T21:13:04.767

Reputation: 10 647

1You could save 2 bytes by doing return r instead of return r>0 at the end. – Sara J – 2019-10-28T15:19:40.683

1@SaraJ Indeed, I thought it was true/false because of the examples in the challenge. The output format is nice for once ;-) – Olivier Grégoire – 2019-10-29T10:38:42.603

1

Wolfram Language (Mathematica), 30 bytes

#2/.{p__,__,s__}/;{p,s}==#->0&

Try it online!

Takes two lists of characters as input. Returns 0 if the first string is a circumfix of the second, or the second string otherwise.

attinat

Posted 2019-10-26T21:13:04.767

Reputation: 3 495

1

Regex, 17 bytes

Takes the two strings in joined format, delimited by a single newline.

^(.+)(.+)
\1.+\2$

Turned out to be identical to Neil's Retina answer (except I chose to separate with a newline), so I'm mainly answering this to demonstrate it in a wide range of regex engines:

Try it online! - ECMAScript - SpiderMonkey
Try it online! - ECMAScript - Node.js
Try it online! - Perl 5
Try it online! - PCRE2 - PHP
Try it online! - .NET - C# (.NET Core)
Try it online! - Java (JDK)
Try it online! - Python 3
Try it online! - Ruby
Try it online! - POSIX ERE (grep -E), tab as delimiter

Deadcode

Posted 2019-10-26T21:13:04.767

Reputation: 3 022

0

APL (Dyalog Unicode), 21 bytesSBCS

Anonymous infix function. Takes strings 1 and 2 as left and right arguments. Requires ⎕IO←0.

{(⊂⍺)∊∊¨↑∘⍵¨¨(1↓⍳≢⍺)-⊂0,~/≢¨⍺⍵}

Try it online!

{} "dfn"; and are is strings 1 and 2:

⍺⍵ strings 1 and 2

≢¨ length of each

~/ remove elements from the first that are in the second (gives empty list if same length)

0, prepend zero

 enclose to treat as a whole

()- subtract that from the following:

  ≢⍺ length of string 1

   indices zero through that

  1↓ drop the first one (the zero)

This gives us the head-tail pairs to try.

¨¨ for element of each of the head-tail pairs:

  ↑∘⍵ take that many characters from string 2 (from the end if negative)

∊¨ϵnlist (flatten) each

()∊ is the following an ϵlement of that?

  ⊂⍺ the entire string 1

Adám

Posted 2019-10-26T21:13:04.767

Reputation: 37 779