13
\$\begingroup\$

Strings with Context

For the purposes of this challenge, a string with context is a triple of strings, called the left context, the data part, and the right context. It represents a substring of a longer string. We use the vertical pipe | as a separator, so an example of a string with context is cod|e-go|lf, where the left context is cod, the data is e-go, and the right context is lf. This example represents the substring e-go of code-golf.

Now, to concatenate two strings with context, we proceed as follows, using aa|bcc|dee and cc|de|eee as examples. We align the strings as in the diagram

a a|b c c|d e e
      c c|d e|e e e

so that their data parts are adjacent. The data part of the concatenation is the concatenation of the data parts, in this case bccde. The left context is the part that extends the furthers to the left of the first data part, in this case aa. Similarly, the right context is eee, so the concatenation is the string with context aa|bccde|eee. For a second example, consider a|bb|cd and aabb|cd|, where the second word has a empty right context. The alignment diagram is

  a|b b|c d
a a b b|c d|

where the left context of the second word extends further than that of the first. The concatenation is aa|bbcd|.

But wait, there's a gotcha: if the letters of the alignment diagram don't match, the concatenation doesn't exist! As an example, the diagram of aa|bb|cc and c|c|c is

a a|b b|c c
      c|c|c

where the b and c on the fourth column disagree, so they cannot be concatenated.

The Task

Your job is to write a program that takes in two strings with context whose parts are separated by | as above, and outputs their concatenation if it exists, and something else if not. The "something else" can be any value, including no output, as long as it's not a valid string with context, and it is the same in all cases. However, throwing an error is not acceptable. You can give either a STDIN-to-STDOUT program or a function, and anonymous functions are accepted as well. The smallest byte count wins, and standard loopholes are disallowed.

Test Cases

aa|bcc|dee cc|de|eee -> aa|bccde|eee
a|bb|cd    aabb|cd|  -> aa|bbcd|
a|b|cccd   aab|cc|c  -> aa|bcc|cd
a|b|c      b||cd     -> a|b|cd
aa|bb|cc   c|c|c     -> None
aaa|b|c    abb|cd|d  -> None
|bb|cd     abb|c|ed  -> None
a|b|c      a||cd     -> None
\$\endgroup\$
0

2 Answers 2

4
\$\begingroup\$

Haskell, 184 182 201 199 155

s&t|(a,'|':b)<-f t,(x,'|':y)<-f$r s,x#b,a#y=r(y!r a)++b!r x|0<1=""
r=reverse
a!b=a++drop(length a-1)b
(#)a=and.zipWith(==)(r a).filter h
f=span h
h=(/='|')

example run:

"|a|"&"|b|" -- returns "|ab|"
"|a|x"&"|b|" -- returns ""

if there isn't a match an empty string will be returned. otherwise the result would be returned.

partial explanation:

# is a function which gets two strings, and returns whether or not they match.

! gets two strings and returns the first one concatenated with extra character from the second (if there are any).

the main function & uses span (/='|') to split the inputs into two parts, a|b|c to a, b|c, checks if the contexts match, and then uses ! twice to assemble the output.

Edit: mage-late regolfing seems to be pretty effective.

\$\endgroup\$
3
  • \$\begingroup\$ Hmm, I'm afraid that throwing an error is not an acceptable output method, especially for a function. Adding |1<2="" to the definition of & should solve that. I'm sorry I didn't specify this more explicitly in the specs, I'll edit it in. \$\endgroup\$
    – Zgarb
    Dec 2, 2014 at 11:11
  • \$\begingroup\$ @Zgarb Actually, this would not fix it. Is returning a string with too many '|' signs when the strings don't match okay? \$\endgroup\$ Dec 2, 2014 at 12:39
  • \$\begingroup\$ Sure, as long as it is the same string for all non-matching inputs. \$\endgroup\$
    – Zgarb
    Dec 2, 2014 at 12:43
3
\$\begingroup\$

Python (242 bytes)

import itertools as i
s='|'
j=''.join
r=reversed
m=lambda a,b:j(j(*set(p+q))for p,q in i.izip_longest(a,b,fillvalue=''))
def c(A,B):
 u,v,w,x,y,z=(A+s+B).split(s)
 try:return j(r(m(r(u+v),r(x))))[:-len(v)]+s+v+y+s+m(w,y+z)[len(y):]
 except:0

Explanation

The lambda function m returns the longer of two strings as long as they share a common prefix. It does this by concatenating the empty string '' in place of any missing values, then turning the result (which may take the forms aa, ab, a, or b in cases of match/mismatch/unequal lengths) into a set of unique characters at each position. join expects a single argument, so unpacking a set with more than one element will cause it to raise a TypeError.

The main function then

  • uses m to combine the left context and data part the first word with the left context of the second (from right to left over reversed strings)
  • concatenates data parts,
  • and again uses m to combine the right context of the first word with the data part and right context of the second

The two original words' data parts are trimmed from the right and left sides of the new contexts.

Since we know that misalignments cause m to raise a TypeError, in these cases we catch the exception and implicitly return None.

Testing

TESTCASES = [
    ('aa|bcc|dee', 'cc|de|eee', 'aa|bccde|eee'),
    ('a|bb|cd', 'aabb|cd|', 'aa|bbcd|'),
    ('a|b|cccd', 'aab|cc|c', 'aa|bcc|cd'),
    ('a|b|c', 'b||cd', 'a|b|cd'),
    ('aa|bb|cc', 'c|c|c', None),
    ('aaa|b|c', 'abb|cd|d', None),
    ('|bb|cd', 'abb|c|ed', None),
    ('a|b|c', 'a||cd', None),
]

for A, B, R in TESTCASES:
    print '{:<10} {:<9} -> {}'.format(A, B, c(A, B))

Output

aa|bcc|dee cc|de|eee -> aa|bccde|eee
a|bb|cd    aabb|cd|  -> aa|bbcd|  
a|b|cccd   aab|cc|c  -> aa|bcc|cd 
a|b|c      b||cd     -> a|b|cd    
aa|bb|cc   c|c|c     -> None      
aaa|b|c    abb|cd|d  -> None      
|bb|cd     abb|c|ed  -> None      
a|b|c      a||cd     -> None  
\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.