Find the Translation Table

17

1

Given two strings, find the translation table (substitution cipher) between the two, if the translation is not possible, output false. The answer must be minimized and created from left-to-right. The first character to be translated between words must be the first in the translation table. In addition to this, any letter that is not translated (in the same place as it was originally), should NOT be in the translation table.

Probably most easily defined through examples:

Valid Cases

"bat", "sap" => ["bt","sp"]

Notice the ordering, an output of ["tb","ps"] is not valid for this challenge.

"sense", "12n12" => ["se","12"]

Notice how the n isn't translated because it is a 1 to 1 relation.

"rabid", "snail" => ["rabd","snal"]

Notice how the i isn't translated because it is a 1 to 1 relation.

"ass", "all" => ["s","l"]

A is not included, it stays the same, s can map to l due to pattern match.

"3121212", "ABLBLBL" => ["312","ABL"]

Matches pattern perfectly.

Falsy Cases

"banana", "angular" => false

(not the same length, impossible).

"animal", "snails" => false

(each character can only be used ONCE on each side of the translation).

"can","cnn" => false

(n is implicitly used in translation, therefore, defining a translation table with n->a would be invalid)

Thusly, [aimal,sails] is an invalid answer, making this falsy.

"a1", "22" => false

See "caveats", this is listed as falsy. In this case, it's because a and 1 cannot both map to 2. (Each character can only be used ONCE on each side of the translation).


This answer seems to be a good benchmark: https://codegolf.stackexchange.com/a/116807/59376

If you have questions about the functionality of two unlisted word pairs, defer to this implementation.


I/O rules

  • Input may be as a 2 element array or as 2 separate inputs.
  • Output can be as an array or newline/space delimited, similar to how I have it shown.
  • False output may be 0, -1 or false. Erroring/Empty output is also fine.
  • You are guaranteed that a will not equal b and neither a nor b will be empty.
  • a and b are printable-ASCII-only sequences of letters.

Caveats

  • Translations must occur from left to right, see example 1.
  • You must not output characters that remain the same.
  • Your program may only take in two strings a and b.
  • Each character can only be used ONCE on each side of the translation. This is what makes the translation from snails to animals impossible.
  • Recursive replaces should not occur. Example of recursive replace: "a1","22"->[a1,12] where a is first replaced by a 1, then both resultant 1's are replaced with 2's. This is not correct, assume all translations occur independent of each other, meaning this is falsy. Meaning: "a1" with translation table of [a1,12] evaluates to 12 (not 22)

Magic Octopus Urn

Posted 2017-04-17T14:56:04.523

Reputation: 19 422

Labeling this "translation" as a simple substitution cipher might help clarify the intent. – Greg Martin – 2017-04-17T16:47:13.473

Are associative arrays allowed as output? It could save me some bytes – Jörg Hülsermann – 2017-04-19T17:47:24.257

@JörgHülserman I'm not fully sure of the implications of allowing this, maybe make 2 versions so I can see the difference? I'll edit it if I think it isn't harmful to the challenge. – Magic Octopus Urn – 2017-04-19T17:52:57.993

Look at my post first solution as string and the second has as output an associative array – Jörg Hülsermann – 2017-04-19T18:01:26.047

@JörgHülsermann ahhh... I see how you're using it now, I think I'm going to disallow that, not all languages support hash-like structures. – Magic Octopus Urn – 2017-04-19T18:03:07.200

Is an underscore allowed as separator? It would save me 2 Bytes – Jörg Hülsermann – 2017-04-19T23:37:37.373

not all languages support hash-like structures Not all languages natively support matrices (PHP for example does not, while most Eso-Langs do); but I haven´t seen them disallowed anywhere. – Titus – 2017-04-20T13:01:27.207

@Titus JavaScript's basic arrays are also technically hash-like structures as well – fəˈnɛtɪk – 2017-04-20T13:26:19.373

Answers

7

PHP >=7.1, 130 Bytes

18 Bytes saved by @Titus

for([,$x,$y]=$argv;a&$o=$y[$i];)$o==($p=$x[$i++])?:$k[$c[$p]=$o]=$p;echo$y==strtr($x,$c)&$x==strtr($y,$k)?join($k)." ".join($c):0;

Testcases

Expanded

for([,$x,$y]=$argv;a&$o=$y[$i];)
$o==($p=$x[$i++])?:$k[$c[$p]=$o]=$p; # if char string 1 not equal char string 2 make key=char1 value=char2 and key array
echo$y==strtr($x,$c) # boolean replacement string 1 equal to string 2
    &$x==strtr($y,$k) # boolean replacement string 2 equal to string 1
    ?join($k)." ".join($c) # output for true cases
:0; #Output false cases

PHP >=7.1, 148 Bytes

prints 0 for false Output true as string

for([,$x,$y]=$argv;a&$o=$y[$i];$i++)$x[$i]==$o?:$c[$x[$i]]=$o;echo$y==strtr($x,($f=array_flip)($k=$f($c)))&$x==strtr($y,$k)?join($k)." ".join($c):0;

Testcases

Expanded

for([,$x,$y]=$argv;a&$o=$y[$i];$i++)
$x[$i]==$o?:$c[$x[$i]]=$o; # if char string 1 not equal char string 2 set key=char1 value=char2
echo$y==strtr($x,($f=array_flip)($k=$f($c))) # boolean replacement string 1 equal to string 2
&$x==strtr($y,$k) # boolean replacement string 2 equal to string 1
    ?join($k)." ".join($c) # output for true cases
:0; #Output false cases

PHP >=7.1, 131 Bytes

The second answer can be shorted to this if associative arrays are allowed

prints 0 for false Output true as associative array instead of string

for([,$x,$y]=$argv;a&$o=$y[$i];$i++)$x[$i]==$o?:$c[$x[$i]]=$o;print_r($y==strtr($x,($f=array_flip)($f($c)))&$x==strtr($y,$k)?$c:0);

Testcases

PHP >=7.1, 227 Bytes

prints 0 for false

[,$x,$y]=$argv;echo strlen($x)==strlen($y)?strtr($x,$c=array_filter(($f=array_flip)($z=$f(array_combine(($p=str_split)($x),$p($y)))),function($v,$k){return$k!=$v;},1))==$y&$x==strtr($y,$z)?join(array_keys($c))." ".join($c):0:0;

Testcases

Expanded

[,$x,$y]=$argv; # 
echo strlen($x)==strlen($y) #compare string lengths
?strtr($x,  # replace function
$c=array_filter( # filter 
($f=array_flip)($z=$f( # # remove doubles like in testcase: a1 => 22
    array_combine(($p=str_split)($x),$p($y))  # replacement array keys string 1 values string 2 
))
    ,function($v,$k){return$k!=$v;},1)) # remove all keys that equal to values in array
    ==$y # boolean replacement string 1 equal to string 2
&$x==strtr($y,$z) # boolean replacement string 2 equal to string 1        
?join(array_keys($c))." ".join($c) # output for true cases
    :0 # Output if replacement from string 1 is not equal to string 2
:0; #Output for different lengths

Jörg Hülsermann

Posted 2017-04-17T14:56:04.523

Reputation: 13 026

You are an extremely experienced PHP developer, I am entirely impressed by that 144-byte one-liner. – Magic Octopus Urn – 2017-04-17T19:19:06.637

1@carusocomputing Thank you for the compliment. I have forget a little trick. I Know that here are many people they are better then me – Jörg Hülsermann – 2017-04-17T19:57:08.430

2Your definitely good at finding solutions +1 but array_values() inside join() is completly useless and can be dropped. – Christoph – 2017-04-18T12:16:48.603

@Christoph yes I am an idiot. Thank You – Jörg Hülsermann – 2017-04-18T12:24:06.330

1This fails the a1 22 => false test case. Also, your first program doesn't seem to work in the online tester. – mbomb007 – 2017-04-18T14:34:55.560

@mbomb007 Try it now Thank you very much. The Joke with the double array_flip to remove these doubles in the values in my bigger solution is more worth as the few bytes – Jörg Hülsermann – 2017-04-18T17:56:18.513

WTG I think this is the first time you beat me. And you can save another byte with echo$y==. Care to add <!-- language --> tags? – Titus – 2017-04-19T10:35:30.203

@Titus done I am not sure that I beat you. It is very complex and the trick with the double array_flip has saved much bytes. and your experience is better as my – Jörg Hülsermann – 2017-04-19T11:05:56.257

1Shut up. It´s marvellous. – Titus – 2017-04-19T11:41:35.580

1The flippers can go: Save 18 bytes with ($p=$x[$i])==$o?:$k[$c[$p]=$o]=$p; in the loop and $y==strtr($x,$c) for the first test. – Titus – 2017-04-19T22:31:40.993

@Titus now you have beat my marvellous Solution . Congratulations. Now I hate really that associative arrays as output are not allowed – Jörg Hülsermann – 2017-04-19T23:35:44.450

Output can be as an array ... I´ll help clearing that subject. And I didn´t beat you; You still have a very nice and short solution with strtr (which I unfortunately completely ignored in my approach). – Titus – 2017-04-20T12:57:29.317

@Titus your improvement count for me as a beat. – Jörg Hülsermann – 2017-04-20T13:04:58.780

7

JavaScript (ES6), 128 bytes

f=
(s,t)=>!t[s.length]&&[...s].every((c,i)=>n[d=t[i]]==c||d&&!m[c]&&!n[d]&&(n[m[c]=d]=c,c==d||(a+=c,b+=d)),m={},n={},a=b='')&&[a,b]
<div oninput=o.textContent=f(s.value,t.value)><input id=s><input id=t><pre id=o>

Neil

Posted 2017-04-17T14:56:04.523

Reputation: 95 035

Doesn't work for ass and all, should be s,l. – Magic Octopus Urn – 2017-04-17T18:36:40.620

Yeah, that snippet be trippin', also verified, it's fine. – Magic Octopus Urn – 2017-04-17T19:08:20.253

1@carusocomputing It was a variable name clash - it's fixed now. Sorry about that. – Neil – 2017-04-17T19:15:26.210

7

JavaScript (ES6), 108 107 105 106 bytes

Edit: Fixed to support inputs such as "22" / "a1" that should be falsy.


Returns either 0 or an array of two strings.

f=(a,b,x)=>[...a].some((c,i)=>d[C=b[i]]?d[C]!=c:(d[C]=c)!=C&&(s+=c,t+=C,!C),s=t='',d=[])?0:x||f(b,a,[s,t])

Formatted and commented

f = (                       // given:
  a,                        // - a = first string
  b,                        // - b = second string
  x                         // - x = reference result from previous iteration,
) =>                        //       or undefined
  [...a].some((c, i) =>     // for each character c at position i in a:
    d[                      //   if we already have a translation
      C = b[i]              //   of the character C at the same position in b,
    ] ?                     //   then:
      d[C] != c             //     return true if it doesn't equal c
    :                       //   else:
      (d[C] = c) != C &&    //     store the translation C -> c in the dictionary
      (                     //     if the characters are different:
        s += c, t += C,     //       append them to the translation strings s and t
        !C                  //       return true if C is undefined
      ),                    //
    s = t = '', d = []      //   initialize s, t and d  
  ) ?                       // if some() returns true:
    0                       //   there was a translation error: abort
  :                         // else:
    x ||                    //   if this is the 2nd iteration, return x
    f(b, a, [s, t])         //   else do a recursive call with (b, a)

Test cases

f=(a,b,x)=>[...a].some((c,i)=>d[C=b[i]]?d[C]!=c:(d[C]=c)!=C&&(s+=c,t+=C,!C),s=t='',d=[])?0:x||f(b,a,[s,t])

// truthy
console.log(f('bat','sap'))
console.log(f('sense','12n12'))
console.log(f('rabid','snail'))
console.log(f('ass','all'))
console.log(f('3121212','ABLBLBL'))

// falsy
console.log(f('banana','angular'))
console.log(f('animal','snails'))
console.log(f('a1','22'))
console.log(f('22','a1'))
console.log(f('aaa','bab'))
console.log(f('abc','abcd'))
console.log(f('abcd','abc'))

Arnauld

Posted 2017-04-17T14:56:04.523

Reputation: 111 334

f('22')('a1') should be falsy too. – Neil – 2017-04-18T12:00:39.683

Hopefully I got it right this time. – Arnauld – 2017-04-18T15:13:50.110

1It's always handy when your bugfix turns out to simplify to something shorter! – Neil – 2017-04-18T15:41:23.137

5

Retina, 194 191 185 229 225 241 bytes

.+
$&;$&
+`^\w(\w*;)\w
$1
^;\w.*|.+;;.*|;;

^((.)*)(.)(.*;(?<-2>.)*(?(2)(?!)))\3
$1$4
+`((.)(.)*)\2((.)*;.*(.)(?<-3>.)*(?(3)(?!)))\6((?<-5>.)*(?(5)(?!)))$
$1$4$7
^(.)*(.)(.)*(\2)?.*;(?<-1>.)*(?(1)(?!))(.)(?<-3>.)*(?(3)(?!))(?(4)(?!\5)|\5).*

Try it online!

Takes input ;-separated. Output is also ; separated. False inputs are signified by empty outputs.

I know this is painfully verbose, I am still trying to cut down bytes. Most of these bytes go into deleting false inputs.

Edits

  • It turns out that I had a significant flaw with my program. It's fixed now, but at the cost of over 40 bytes.

  • Another mistake was found where my program did not declare the input a1;22 false, but I was able to keep the program under 250 bytes after fixing it

Explanation

(a more detailed explanation will be coming shortly)

First we have to check if the lengths of strings a and b are the same or not. If they are not, we delete everything.

Duplicates the input to preserve it while we do some length-testing.

.+                      Matches everything
$&;$&                   $& indicates the match, so $&;$& will duplicate the match and separate it with a semi-colon

Now in a loop, we delete the first character of a and the first character of b until one of the strings become empty.

+`                     Repeatedly (until no more substitutions could be made) replace
  ^\w                   A word character (letter or number) at the beginning
     (\w*;)             Capture Group 1: matches any number of word characters and a semicolon
           \w           And a word character after the semi-colon
                       with
$1                      The result of the first capture group

Now there are there possibilities for the "pattern space".

  • ;;abc Both strings are of equal length
  • def;;abc a is longer than b
  • ;def;abc b is longer than a

Now we have to empty the input if the strings are not of the same length (scenarios 2 and 3). This is what this substitution below does. It removes text that matches scenarios 2 and 3.

^;\w.*|.+;;.*|;;

This removes characters that are not transliterated in strings a and b. abc;1b2 => ac;12

^((.)*)(.)(.*;(?<-2>.)*(?(2)(?!)))\3
$1$4

After that, we have to remove duplicate characters. sese;1212 => se;12, but this preserves inputs like aba;123

+`((.)(.)*)\2((.)*;.*(.)(?&lt;-3&gt;.)*(?(3)(?!)))\6((?&lt;-5&gt;.)*(?(5)(?!)))$
$1$4$7

Finally, we delete the input if there are duplicate characters that map to different characters like aba;123 or a1;22.

^(.)*(.)(.)*(\2)?.*;(?.)*(?(1)(?!))(.)(?.)*(?(3)(?!))(?(4)(?!\5)|\5).*

And finally, remove duplicate characters.

user41805

Posted 2017-04-17T14:56:04.523

Reputation: 16 320

Using those balancing groups I see! – Neil – 2017-04-17T17:03:52.217

@Neil Indeed! I also used the (?(1)(?!)) I learnt from your answer :) – user41805 – 2017-04-17T17:04:39.830

I wonder whether it would be shorter to remove duplicates first and then validate the remaining sets - there should only be one of each letter remaining on each side of the ;. – Neil – 2017-04-17T18:22:53.300

@Neil I found an error with my code experimenting with that. I will look further into it in the morning. – user41805 – 2017-04-17T18:54:04.307

5

Jelly, 18 bytes

ẠaQ⁼µ€Ạ
z0EÐḟQZẋÇ$

Unnamed monadic link (one-input function) taking a list, which returns:
an empty list in the falsey cases; or
a list containing two lists of characters in the truthy cases.

Try it online! (the footer splits the list with a space to avoid printing a smushed representation)
...or see a test suite.

How?

ẠaQ⁼µ€Ạ - Link 1, valid?: mapping list
    µ€  - perform the code to the left for €ach mapping entry
Ạ       -     none of mapping entry falsey? (this & Main's z0 handle unequal input lengths)
  Q     -     deduplicate mapping entry
   ⁼    -     is equal to mapping list? (non-vectorising)
 a      -     and
      Ạ - none falsey (both mapping lists must pass that test)
        - The whole function returns 1 if the mapping list is acceptable, 0 if not

z0EÐḟQZẋÇ$ - Main link: list of strings
z0         - transpose with filler 0 (unequal lengths make pairs containing zeros)
   Ðḟ      - filter discard:
  E        -     all equal? (removes the untranslated character pairs)
     Q     - deduplicate (removes the repeated translation pairs)
      Z    - transpose (list of pairs to pair of lists)
         $ - last two links as a monad:
       ẋ   -     repeat list this many times:
        Ç  -         call last link (1) as a monad

Jonathan Allan

Posted 2017-04-17T14:56:04.523

Reputation: 67 804

4

Jelly, 28 26 bytes

QL$€⁼L€
EÐḟQZK0Ç?
ZÇ0L€E$?

Try it online!

QL$€⁼L€      Checks validity of mapping
QL$€          number of unique characters in mapping
    ⁼         equals
     L€       number of characters in mapping

EÐḟQZK0Ç?  Writes valid mapping or 0
EÐḟ           filter maps where a = b
   Q          filter duplicate maps
    Z         zip by column [["ac"],["bd"]] => ["ab","cd"]
     K0Ç?   print if valid map, else print 0

ZÇ0L€E$?      main link: takes an array of 2 strings
Z              zip by column: ["ab", "cd"] => [["ac"],["bd"]]
 Ç     ?       print mapping if
   L€E$         all pairs are same length (returns 0 if initial strings were
  0             else 0

layagyasz

Posted 2017-04-17T14:56:04.523

Reputation: 111

1Welcome to PPCG! How the hell do you already know Jelly with only 21 points? Very impressive! – Magic Octopus Urn – 2017-04-17T20:34:16.430

2Thanks. Looked around the site a bit and it seemed like a neat language to learn. – layagyasz – 2017-04-17T21:36:28.200

05AB1E is another easy and fun one to try out. – Magic Octopus Urn – 2017-04-18T13:20:25.693

3

Ruby, 133 bytes

->a,b{a.size!=b.size||(m=a.chars.zip b.chars).any?{|i,j|m.any?{|k,l|(i==k)^(j==l)}}?0:m.select{|x,y|x!=y}.uniq.transpose.map(&:join)}

Try it online!

More readably:

->a, b{
    # Pair the letters in each string - [AB, AB, AB,...]
    pairs = a.chars.zip(b.chars)

    # If there's any combination of two pairs that share one character but not both,
    # or if the strings have different lengths, then the input's invalid.
    if a.size != b.size || pairs.any?{|i,j| pairs.any? {|k, l| (i==k)!=(j==l) }} 
        return 0 # 0 isn't actually falsy in Ruby, but this challenge allows it anyway
    end
    return pairs.select{|x,y| x != y} # Remove unchanged letters
                .uniq                 # Remove duplicates
                .transpose            # Change [AB, AB, AB] form to [AAA, BBB] form.
                .map(&:join)          # Convert the arrays back into strings
}

Just for kicks, here's an 84 byte version in Goruby, which is Ruby, but with a golf flag set when compiling the interpreter. Among other things, it allows you to abbreviate method calls to their shortest unique identifier.

->a,b{a.sz!=b.sz||(m=a.ch.z b).ay?{|i,j|m.y?{|k,l|(i==k)^(j==l)}}?0:m.rj{|x,y|x==y}.u.tr.m(&:j)}

Tutleman

Posted 2017-04-17T14:56:04.523

Reputation: 571

Why not post a second answer with the Goruby implementation? Is it not an accepted golfing language? – Magic Octopus Urn – 2017-04-17T17:57:50.267

@carusocomputing It totally is; it just seemed to me like it didn't merit its own answer - it's exactly the same as my main answer, just with method names abbreviated. Perhaps if I find a way to take advantage of more of Goruby's differences I'll post a separate answer. – Tutleman – 2017-04-17T21:24:50.030

3

Python 3.6, 211 185 181 178 bytes

Exits with an error for falsy results.

def f(x,y,d={}):
    for a,b in zip(x,y):1/(a not in d or b==d[a]or len(x)-len(y));d[a]=b;1/([*d.values()].count(b)<2)
    return map(''.join,zip(*[x for x in d.items()if x[0]!=x[1]]))

This requires Python 3.6, which you can run in a shell here.

You can test it without the correct output ordering on TIO here. (TIO doesn't have 3.6).

Ungolfed:

from collections import*
d=OrderedDict()                     # keep order
x,y=input()
if len(x)!=len(y):1/0               # equal lengths
for a,b in zip(x,y):
    if a in d and d[a]!=b:1/0       # no duplicate keys
    else:d[a]=b
    if d.values().count(b)>1:1/0    # no duplicate values
print map(''.join,zip(*[x for x in d.items()if x[0]!=x[1]])) # format, no no-ops

If only order didn't matter...

mbomb007

Posted 2017-04-17T14:56:04.523

Reputation: 21 944

Shouldn't a1,12 return a1,12 instead of False? Under the Caveats section it is said that "a1" with translation table of [a1,12] evaluates to 12. – fergusq – 2017-04-18T07:37:28.850

1Well, the program in your TIO link returns False. 1a 21 would also be wrong, as the oeder has to be preserved. – fergusq – 2017-04-18T15:44:27.147

@fergusq Fixed. But notice that you have a typo in your comment if that's the test case you're referring to, since you said a1,12 instead of a1,22. – mbomb007 – 2017-04-18T16:01:53.337

I misunderstood you. You referred to the Caveats section in your question edit, but the Caveats section actually handles a different case – not the bijection rule. That confused me. – fergusq – 2017-04-18T16:32:58.133

It handles a different rule, but it still says that the result of that test case is false, which is what mattered. – mbomb007 – 2017-04-18T16:40:30.607

In Python 3.6, regular old dicts preserve order. It's an implementation detail, but it'd save you a few bytes to use it vs Python 2 because you wouldn't need to import anything. – Tutleman – 2017-04-19T17:17:49.937

Oh, and I think 1/(a not in d or d[a]==b) can be replaced with 1/(not d.get(a)), because dict.get() returns None if the key isn't present. It's totally possible I didn't think the edge cases through on this one, though. – Tutleman – 2017-04-19T17:22:50.580

That last change fails the "rabid", "snail" test case. – mbomb007 – 2017-04-19T18:08:33.663

You can do [*d.values()] instead of list(d.values()), too, to save 3 bytes, I believe. – Tutleman – 2017-04-19T19:45:17.517

3

Python 2, 198,193,189,182,179,175,169,165 bytes

def f(a,b):
 r=([""]*2,0)[len(a)!=len(b)]
 for u,v in zip(a,b):
	if r:
		q,w=r
		f=q.find(u)
		if u!=v:r=(([q+u,w+v],r)[f>-1 and w[f]==v],0)[f<0 and v in w]
 print r

Try it online!

  • -4 bytes! thanks to mbomb007 for suggesting the use of tab instead of space.

  • modified the input format, again thanks to mbomb007.

Keerthana Prabhakaran

Posted 2017-04-17T14:56:04.523

Reputation: 759

what do you mean by that? please stop making unwanted edits which doesnt add any value to answer! – Keerthana Prabhakaran – 2017-04-18T15:58:54.927

tab saved around 4 bytes! Thank you! – Keerthana Prabhakaran – 2017-04-18T16:02:48.423

Let us continue this discussion in chat.

– mbomb007 – 2017-04-18T16:03:58.233

And I made your program have each test case on a single line, which is extremely helpful to anyone testing your program. – mbomb007 – 2017-04-18T16:07:10.760

I'd would have better if you could have mentioned that in your edit comment! – Keerthana Prabhakaran – 2017-04-18T16:18:26.717

It's bad practice to have a variable f inside a function f. Also, it's probably shorter to use a,b=input() instead of declaring a function, because then you have less indentation. – mbomb007 – 2017-04-18T18:49:25.457

@mbomb007 Isn't everything we do here bad practice? – L3viathan – 2017-04-19T11:03:55.883

@L3viathan Hey, if you can avoid it without costing bytes, then no. – mbomb007 – 2017-04-19T14:04:35.403

2

Röda, 108 119 bytes

{c=[{_<>_|[[_,_]]|orderedUniq}()]d=[]e=[]c|_|{{d+=a;e+=b}if[a!=b]}for a,b[d,e]if[0,1]|{|n|c|[_[n]]|sort|count|[_2=1]}_}

Try it online!

This is a function that takes two lists of characters from the stream and pushes two lists to the stream.

This could be sorter if I was allowed to return pairs.

Explanation (out-dated):

{
    c=[{
        _<>_|       /* pull two lists and interleave them */
        [[_,_]]|    /* "unflat", create lists from pairs */
        orderedUniq /* remove duplicates */
    }()]            /* c is a list of the pairs */
    d=[]
    e=[]
    c| /* push the pairs to the stream */
    _| /* flat */
    {  /* for each pair (a, b): */
        { /* if a != b (remove "1-to-1 relations"):  */
            d+=a;
            e+=b
        }if[a!=b]
    }for a,b
    /* return d and e if no character is mapped to more than one character */
    [d,e]if c|[_[0]]|sort|count|[_2=1]
}

Here's an underscore solution that contains no variables (114 bytes):

{[[{_<>_}()|[[_,_]]|unorderedUniq]]|[[_()|_|[_]if[_1!=_2]],[_1()|_|[_2]if[_1!=_2]]]if[[_1()|_][::2],[_1()|_][1::2]]|[sort(_)|count|[_2=1]]}

That's a lot of underscores.

fergusq

Posted 2017-04-17T14:56:04.523

Reputation: 4 867

What does the <> do? – user41805 – 2017-04-18T06:37:32.603

@KritixiLithos It's the interleave operator. a() <> b() is same as interleave([a()], [b()]) (or just interleave(a, b), if a and b are arrays). – fergusq – 2017-04-18T06:53:16.710

This fails the a1 22 => false test case. "all translations occur independent of each other, meaning this is falsy." – mbomb007 – 2017-04-18T14:30:43.107

@mbomb007 I don't quite understand what you say? Do you mean it has to be a bijection, ie. no two characters must be mapped to a same character? – fergusq – 2017-04-18T15:48:42.823

Yes. That's what the question says. (Each character can only be used ONCE on each side of the translation) – mbomb007 – 2017-04-18T15:57:56.843

@mbomb007 Fixed. – fergusq – 2017-04-18T16:27:46.927

1

AWK, 140 bytes

BEGIN{RS="(.)"}RT~/\W/{S=1}RT~/\w/&&S{if(RT!=x=A[++b]){if(B[z=RT]==""){B[z]=x
c=c x
d=d z}a=B[z]!=x?0:a}}!S{A[++a]=RT}END{if(a==b)print c,d}

Usage: Place code in FILE then:

awk -f FILE <<< "string1 string2"

The input strings need to be whitespace separated.

The output is empty if they fail, or 2 strings separated by a space.

Robert Benson

Posted 2017-04-17T14:56:04.523

Reputation: 1 339

1

k, 28 bytes

{$[(y?y)~x?x;+?(~=/)#x,'y;]}

Explanation:

{                          } /function that takes in two strings, x and y
 $[         ;            ;]  /if statement (to check if there is a mapping)
         x?x                 /first index of [each letter in x] in x
   (y?y)                     /first index of [each letter in y] in y
        ~                    /make sure they match
                     x,'y    /zip together the two strings
               (~=/)#        /remove equal pairs
              ?              /unique pairs only
             +               /transpose ("unzip", in a way)

zgrep

Posted 2017-04-17T14:56:04.523

Reputation: 1 291

1

APL (Dyalog) with AGL, 22 bytes

{≡/⍳⍨¨⍺⍵:↓⍉↑∪⍺(≠é,¨)⍵}

Try it online!

{} anonymous function:

 If…

  ⍺⍵ the arguments

  ⍳⍨¨ when self-indexed (i.e. the first occurrences of their elements in themselves)

  ≡/ are equivalent

: then:

  ⍺()⍵ apply the following tacit function to the arguments:

    concatenate corresponding elements (errors on mismatching lengths)

   é then filter by (é is just the primitive function /)

    where the strings are different

   unique (remove duplicates)

  ↓⍉↑ transpose list-of-pairs to pair-of-lists (lit. mix into table, transpose table, split into lists)

 else, do nothing

Adám

Posted 2017-04-17T14:56:04.523

Reputation: 37 779

1patiently awaits the explanation of this answer :P – Magic Octopus Urn – 2017-05-02T22:03:56.850

1@carusocomputing I'm on it. – Adám – 2017-05-02T22:04:25.520

@carusocomputing OK? – Adám – 2017-05-02T22:18:23.960

I missed that reply, sorry! ↓⍉↑ still has me a little confused. – Magic Octopus Urn – 2017-05-03T17:04:46.713

1

@carusocomputing Maybe this helps? Note that in APL and J, a matrix is not the same as a list of lists.

– Adám – 2017-05-03T17:07:55.783

Oh, wow, yeah that definitely helps; and I know very little about the language, it makes a lot more sense now. – Magic Octopus Urn – 2017-05-04T16:38:54.377

0

CJam, 38 bytes

{_:,~={1\/;}:K~z{)\c=!},L|z_{_L|=K}%;}

Input and output are arrays on the stack.

Esolanging Fruit

Posted 2017-04-17T14:56:04.523

Reputation: 13 542

0

PHP (>=7.1), 165 bytes

for([,$x,$y]=$argv;a&$c=$x[$i];$t[$c]=$d)$z+=($d=$y[$i++])&&$d==($t[$c]??$d);foreach($t as$a=>$b)$a==$b?:$r[$a]=$b;print_r($z<$i|array_unique($r)<$t||a&$y[$i]?0:$t);

prints 0 for falsy, associative array else. Run with -r or test it online.

breakdown

for([,$x,$y]=$argv;         # import arguments to $x and $y
    a&$c=$x[$i];            # loop through $x
    $t[$c]=$d)                  # 2. add pair to translation
$z+=                            # 1. increment $z if
    ($d=$y[$i++])&&             # there is a corresponding character in $y and
    $d==($t[$c]??$d);           # it equals a possible previous replacement
                            # remove identities from translation
foreach($t as$a=>$b)$a==$b?:$r[$a]=$b;
print_r(
    $z<$i                   # if not all tests passed
    |array_unique($t)<$t    # or there are duplicates in the translation
    ||a&$y[$i]              # or $y has more characters
    ?0                      # then print 0
    :$r                     # else print translation
);

Titus

Posted 2017-04-17T14:56:04.523

Reputation: 13 814

Are associative arrays as Output allowed? Could you please add that it works above version 7.1 – Jörg Hülsermann – 2017-04-19T11:58:45.367

@JörgHülsermann Output can be as an array or ..., so I´d say yes. Current PHP version is implicit for all my postings; but if I find something important to edit, I´ll add the version. – Titus – 2017-04-19T13:13:48.163

The valid cases shows only one meaning of array output. If associative arrays are also allowed I can save a few bytes. If it is allowed and array_unique($r)!=$r is in every case array_unique($r)<$r I will upvote your post alone for this trick. In the moment I am searching for an explanation – Jörg Hülsermann – 2017-04-19T13:39:48.470

@JörgHülsermann array_unique($t)<$t (had to change that because can to cnn is invalid) works, because array comparison (unlike string comparison) compares lengths before anything else. – Titus – 2017-04-19T16:09:23.717

The test can to cnn cost me 17 Bytes Forget my suggestion – Jörg Hülsermann – 2017-04-19T17:44:43.283