36
\$\begingroup\$

Guidelines

Scenario

John has an important number, and he doesn't want others to see it.

He decided to encrypt the number, using the following steps:

His number is always a non-decreasing sequence (ie. "1123")

He converted each digit into English words. (ie. "123" -> "ONETWOTHREE")

And then, rearrange the letters randomly. (ie. "ONETWOTHREE" -> "ENOWTOHEETR")

John felt that his number were safe in doing so. In fact, such encryption can be easily decrypted :(


Task

Given the encrypted string s, your task is to decrypt it and return the original number.


Rules

  • This is code golf, so the shortest answer in bytes wins
  • You can assume that the input string is always valid
  • The input string only contains uppercase letters
  • The original numbers are always arranged in ascending order
  • You may return the number in string or integer format
  • The letters will only be shuffled between one word, not between the whole string.
  • The numbers will only be from 1 to 9 inclusive (ONE to NINE)

Possible Unscrambled String

Here is a list of the strings just after they have been converted to strings from the numbers:

 1 -> ONE 
 2 -> TWO
 3 -> THREE
 4 -> FOUR
 5 -> FIVE
 6 -> SIX
 7 -> SEVEN
 8 -> EIGHT
 9 -> NINE

Examples

"NEO" -> 1

"ENOWOT" -> 12

"EONOTWHTERE" -> 123

"SNVEEGHEITNEIN" -> 789

"ENOOWTEERHTRUOFEVIFXISNEVESTHGIEENIN" -> 123456789

"NOEWOTTOWHEERT" -> 1223

\$\endgroup\$
  • 5
    \$\begingroup\$ In all the test cases, only the letters within a word are shuffled, not letters between words. Will that always be the case? \$\endgroup\$ – xnor Jul 11 '17 at 7:32
  • 1
    \$\begingroup\$ @xnor That will always be the case. I have edited the question. \$\endgroup\$ – Amorris Jul 11 '17 at 7:39
  • 1
    \$\begingroup\$ then you need to change this "....(ie. "ONETWOTHREE" -> "TTONWOHREEE")" \$\endgroup\$ – J42161217 Jul 11 '17 at 7:41
  • 2
    \$\begingroup\$ @ TessellatingHeckler : A non-strictly increasing sequence is when the next number can be the same as the previous ex. 1-1-1-2-2-3 (non-strictly increasing) as opposed to 1-2-3-4-5 (strictly increasing) \$\endgroup\$ – koita_pisw_sou Jul 11 '17 at 8:37
  • 1
    \$\begingroup\$ Technically speaking, this is an encoding, not encryption, since there is no key. \$\endgroup\$ – Patrick Roberts Jul 11 '17 at 9:10

21 Answers 21

5
+200
\$\begingroup\$

Jelly,  38  37 bytes

ḟ“RGS”O“OX‘,“¢©“¢¢¤‘yF×4/%74ị⁽Gל?9¤Ḍ

A monadic link taking a list of characters (the string) and returning an integer.

Try it online!

Uses a very different method to Pietu1998's Jelly answer, yet has the same byte count (I really thought it might it did end up as less)!

Does not rely on the monotonicity of the original number (so an input of HTREEWTONOE would work for example).

How?

First note that the words themselves (and therefore any anagrams thereof) can all be changed to ones of length 4 by removing any Rs, Gs and Ss and replacing any Os with two characters (say "12") and any Xs with three characters (say "345").

letters  -> -RGS  -> O:12, X:345
ONE         ONE      12NE
TWO         TWO      TW12
THREE       THEE     THEE
FOUR        FOU      F12U
FIVE        FIVE     FIVE
SIX         IX       I345
SEVEN       EVEN     EVEN
EIGHT       EIHT     EIHT
NINE        NINE     NINE

We may then map the product of the ordinals of those characters to the numbers 1 to 9 using modulo arithmetic, depending upon our choice (the "12345"), then look these up in a reordered list of the digits. The code actually casts to characters first and then replaces the ordinals, but it is also possible in 37 bytes with characters, e.g. "DIAAE" (try it).

ḟ“RGS”O“OX‘,“¢©“¢¢¤‘yF×4/%74ị⁽Gל?9¤Ḍ - link: list of characters
 “RGS”                                - literal ['R','G','S']
ḟ                                     - filter discard
      O                               - convert to ordinals
       “OX‘                           - code-page indices list = [79,88]
            “¢©“¢¢¤‘                  - code-page indices lists = [[1,6],[1,1,3]]
           ,                          - pair -> [[79,88],[[1,6],[1,1,3]]]
                    y                 - translate (replace 79s (Os) with [1,6]
                                                       and 88s (Xs) with [1,1,3])
                     F                - flatten into a single list
                       4/             - 4-wise reduce by:
                      ×               -   multiplication (product of each window of four)
                         %74          - modulo 74
                                   ¤  - nilad followed by link(s) as a nilad:
                             ⁽G×      -   base 250 literal = 18768
                                œ?9   -   permutation of [1,2,3,4,5,6,7,8,9] at that
                                      -   index in a lexicographically sorted list of
                                      -   all such permutations -> [1,5,8,2,4,9,7,6,3]
                            ị         - index into
                                    Ḍ - convert from decimal digits to an integer
\$\endgroup\$
  • \$\begingroup\$ Your answer is literally the only answer on this page that returns a correct value for: NINEONENIENOENNNIENOENNEINEONEINEONNENIENOINNEINENINNEINENIENNIENNNNIENNEININENIENNENINEINENINENNIEINNEINNENNIENIN. \$\endgroup\$ – Magic Octopus Urn Aug 11 '17 at 14:53
  • \$\begingroup\$ +Infinity points. \$\endgroup\$ – Magic Octopus Urn Aug 11 '17 at 14:56
  • \$\begingroup\$ Thanks! (that threw me because there are zero width spaces to the code block in the comment, but (whew) it does work) \$\endgroup\$ – Jonathan Allan Aug 11 '17 at 15:14
  • \$\begingroup\$ It's not a valid input anyway ;). \$\endgroup\$ – Magic Octopus Urn Aug 14 '17 at 1:53
  • \$\begingroup\$ Oh wow, I didn't know a bounty was coming - thanks! Yeah, it was not part of the requested spec, I just made a method that would work with unordered input. \$\endgroup\$ – Jonathan Allan Aug 14 '17 at 2:11
10
\$\begingroup\$

Python 2, 121 117 115 bytes

def g(s,a=0,f=''):
 for c in s:
    a+=34**ord(c)%43;r='P!\x83u\x8eI\x92|Z'.find(chr(a))+1
    if r:f,a=f+`r`,0
 return f

-4 bytes: After all that golfing I forgot to inline a single-use variable. Brain fart.
-2 bytes: Double-spaced indent → single tab indent (thanks to Coty Johnathan Saxman); note that this does not display correctly in the answer.

Ungolfed (compatible with python 3):

nums = [80, 33, 131, 117, 142, 73, 146, 124, 90]

def decode(str):
    acc = 0
    final = ''
    for c in str:
        acc += (34**ord(c))%43
        if acc in nums:
            final += str(1+nums.index(acc))
            acc=0
    return final

Magic number finder:

#!/usr/bin/env python3
from itertools import count, permutations

def cumul(x):
    s = 0
    for v in x:
        s += v
        yield s

all_words = 'ONE TWO THREE FOUR FIVE SIX SEVEN EIGHT NINE'.split()

for modulo in range(1, 1000):
    for power in range(1, 300):
        combinations = []
        for word in all_words:
            my_combination = []
            for perm in permutations(word):
                my_combination += cumul(power**(ord(x)) % modulo for x in perm)
            combinations.append(my_combination)

        past_combinations = set(())
        past_intermediates = set(())
        collision = False
        for combination in combinations:
            final = combination[-1]
            if final in past_intermediates or any(intermediate in past_combinations for intermediate in combination):
                collision = True
                break
            past_combinations.add(final)
            past_intermediates.update(combination)

        if not collision:
            print("Good params:", power, modulo)
            print("Results:", ", ".join(str(x[-1]) for x in combinations))

Explanation:

I had a feeling that I could smash the ASCII bits together and sum them up somehow to determine when I had a full word. Originally I tried messing with 3**ord(letter) and comparing to expected results, but it resulted in some very large numbers. I though it would be appropriate to brute-force some parameters a little, namely modulus (to ensure the numbers are small) and a multiplier to disperse the numbers differently around the range of the modulus.

I ended up changing the multiplier variable into a variable affecting the power itself because (from trial and error) that somehow managed to give me a slightly shorter golfed answer.

And above you see the results of that brute-forcing and a little manual golfing.

The reason for choosing 3**x originally is because I knew you could represent every number there. The most repeated digits any number had is two (thrEE, sEvEn, NiNe, etc), so I decided to think of every input as a base-3 number. That way I could (mentally) represent them as something like 10100000000010020000 (three; a 1 in the t slot, a 1 in the r slot, a 1 in the h slot, and a 2 in the e slot). Each number this way gets a unique representation which can be easily pieced together by iterating the string and summing some numbers, and it ends up independent of the actual order of the letters. Of course, this didn't turn out to be the ideal solution, but the current solution is still written with this idea in mind.

\$\endgroup\$
  • \$\begingroup\$ What's Py3K?... \$\endgroup\$ – CalculatorFeline Jul 11 '17 at 16:18
  • \$\begingroup\$ Apologies, edited (it's the former name of python 3) \$\endgroup\$ – Score_Under Jul 11 '17 at 16:19
  • 1
    \$\begingroup\$ It's cheap, but you can save 2 bytes (since this is python 2) by subbing your second indentation level (two spaces) for a single tab. [tio.run/##NU7NCoJAGDy7T/… Try it online!] \$\endgroup\$ – Coty Johnathan Saxman Jul 12 '17 at 7:15
  • \$\begingroup\$ Also, you might be able to save 6 bytes using literal \x83, \x8e, and \x92 in the string. \$\endgroup\$ – CalculatorFeline Jul 12 '17 at 15:36
  • \$\begingroup\$ @CalculatorFeline Unfortunately my interpreter doesn't like that: SyntaxError: Non-ASCII character '\xc2' in file <stdin> on line 3, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details. It works if I put the coding comment up there, but that gains 15 extra bytes. \$\endgroup\$ – Score_Under Jul 12 '17 at 21:54
6
\$\begingroup\$

Python 2,131 127 bytes

s=input()
for y in'WXGURFSOIZ':vars()[y]=s.count(y)
while Z<9:s+=[O-U-W,W,R-U,U,F-U,X,S-X,G,I-X-G-F+U][Z]*str(Z+1);Z+=1
print s

Try it online!

Based on a corrected version of the JavaScript Draco18s solution.

\$\endgroup\$
  • \$\begingroup\$ What an interesting use of vars! \$\endgroup\$ – xnor Jul 11 '17 at 23:24
  • \$\begingroup\$ @xnor it was ovs how learned me that for other golfs :))) \$\endgroup\$ – mdahmoune Jul 12 '17 at 11:48
  • \$\begingroup\$ Very clever. Have a +1 for adapting my answer (as flawed as it was originally). \$\endgroup\$ – Draco18s Jul 12 '17 at 15:44
5
\$\begingroup\$

PHP, 164 bytes

for($c=count_chars($argn);$i<9;)echo str_pad("",[$c[79]-$c[87]-$u=$c[85],$c[87],$c[72]-$g=$c[71],$u,$f=$c[70]-$u,$x=$c[88],$c[86]-$f,$g,$c[73]-$x-$f-$g][+$i],++$i);

Try it online!

PHP, 179 bytes

based on the previous approach check first the even numbers and then the odd numbers in increasing order

for($z=[$o=($c=count_chars($argn))[87],$f=$c[85],$x=$c[88],$g=$c[71],$c[79]-$o-$f,$c[72]-$g,$v=$c[70]-$f,$c[86]-$v,$c[73]-$x-$v-$g];$i<9;)echo str_repeat(++$i,$z[_405162738[$i]]);

Try it online!

PHP, 201 bytes

for(;$o=ord(WUXGOHFVN[$i]);$i++)for(;$r[$o]<count_chars($argn)[$o];$t[]=$i>3?2*$i-7:2+2*$i,sort($t))for(++$r[$o],$n=0;$q=ord(([TO,ORF,IS,HEIT,EN,TREE,IVE,SEEN,NIE][+$i])[$n++]);)$r[$q]++;echo join($t);

Try it online!

\$\endgroup\$
  • \$\begingroup\$ fails for ENOOWTWTOWOT \$\endgroup\$ – Titus Jul 11 '17 at 14:58
  • \$\begingroup\$ @Titus is now fixed. I have misunderstand the question \$\endgroup\$ – Jörg Hülsermann Jul 11 '17 at 17:05
  • \$\begingroup\$ Yea the examples are somewhat misleading. Wow that did cost! Would you break that down?! \$\endgroup\$ – Titus Jul 11 '17 at 17:35
  • \$\begingroup\$ @Titus I think I have reach the limit to find another way as your approach \$\endgroup\$ – Jörg Hülsermann Jul 11 '17 at 18:18
  • 1
    \$\begingroup\$ $i++<9 and $i instead of $i<10 and ++$i (-1 byte); _405162738[$i] instead of $i%2?$i/2+4:$i/2-1 (-4 bytes) ($i/2+~($i%2*-5) would work too, but that´s one byte longer.) \$\endgroup\$ – Titus Jul 11 '17 at 19:09
5
\$\begingroup\$

Javascript (ES6), 288 150 144 bytes

q=s=>[u=(l=t=>s.split(t).length-1)`U`,l`O`-l`W`-u,l`W`,l`R`-w,u,f=l`F`-u,x=l`X`,l`S`-x,g=l`G`,l`I`-x-g-f].map((n,i)=>`${i}`.repeat(i&&n)).join``

const testCases = ['NEO', 'ENOWOT', 'EONOTWHTERE', 'SNVEEGHEITNEIN', 'ENOOWTEERHTRUOFEVIFXISNEVESTHGIEENIN']

testCases.forEach(testCase => console.log(testCase, q(testCase)))

Longer than the other two one of the other JS entries, but I thought I'd drop an interesting approach that might work for someone in another language.

Essentially we can determine the following:

W -> 2
X -> 6
G -> 8
U -> 4

Any occurrence of these letters implies that that digit exists in the original number. From here we can deduce the rest of the digits:

R-U -> 3
F-U -> 5
S-X -> 7

Including the two complicated cases:

O-(U+W) -> 1
I-(X+G+(F-U)) -> 9

Both 1 and 9 area Hard comparatively. For ONE, E shows up more than once in some words (SEVEN has two) as does N (NINE), so we're stuck with checking for O which occurs in two other places, fortunately both are simple.

For NINE, nine is hard no matter how you slice it.

Thus we end up with this map:

[u=(l=t=>s.split(t).length-1)`U`,  //unused 0; precompute 'U's
 l`O`-l`W`-u,    //1
 l`W`,           //2
 l`R`-w,         //3
 u,              //4
 f=l`F`-u,       //5
 x=l`X`,         //6
 l`S`-x,         //7
 g=l`G`,         //8
 l`I`-x-g-f]     //9

9 is able to back-reference siX, eiGht, and Five (with 5 back-referencing foUr) with the variable assignments, saving bytes. Thanks to Neil for this, it uses several features of JS I am very unfamiliar with (the back-ticks for stripping (' in half, for instance) and actually comes much closer to the idea I'd doodled out on paper before attempting to code it (I'd left 9 as "what's left over", thinking about it as "if I see an X I can remove it and an S and I from the string, then..." so that after the four simple cases the next 3 would become simple).

The reason this entry is interesting is because it can handle any shuffled string as input. i.e. rather than the individual words being shuffled, we can shuffle the whole string, which is what I thought John was doing originally:

q=s=>[u=(l=t=>s.split(t).length-1)`U`,l`O`-l`W`-u,l`W`,l`R`-w,u,f=l`F`-u,x=l`X`,l`S`-x,g=l`G`,l`I`-x-g-f].map((n,i)=>`${i}`.repeat(i&&n)).join``

const testCases = ['XENSENINEVSI']

testCases.forEach(testCase => console.log(testCase, q(testCase)))

\$\endgroup\$
  • 1
    \$\begingroup\$ Great, but there is a problem with counting 9... I think it may be i-x-g-f+u \$\endgroup\$ – mdahmoune Jul 11 '17 at 22:43
  • \$\begingroup\$ @mdahmoune Shoot, you're right. I messed that one up. :< \$\endgroup\$ – Draco18s Jul 12 '17 at 1:49
  • \$\begingroup\$ Save 4 bytes by using s.split(t).length-1, 2 bytes using s.repeat(n>0&&n) (why is n less than zero anyway? saves 7 bytes). Save a bunch of bytes by declaring g in the scope of s so that you don't have to keep passing it all the time, and better still you can make it a tagged template, which saves 55 bytes in total (before 9 correction). Save more bytes by saving repeated values in temporaries, and I shaved a few more off using map: s=>[,(l=t=>s.split(t).length-1)`O`-l`W`-l`U`,w=l`W`,l`R`-w,u=l`U`,l`F`-u,x=l`X`,l`S`-x,g=l`G`,l`I`-x-g].map((n,i)=>`${i}`.repeat(n)).join`` . \$\endgroup\$ – Neil Jul 12 '17 at 12:24
  • \$\begingroup\$ @Neil I am not sure why N ever ended up less than zero, but it did when testing for THREE. I kept getting an error and investigating I found that it was needed, but I'm still not sure. The templated library map you've got there is javascript I don't even know how to read. :D \$\endgroup\$ – Draco18s Jul 12 '17 at 13:33
  • \$\begingroup\$ @Neil Ah, right, the reason for checking n > 0: If there is a TWO but no THREE. R = 0, W = 1. 0-1 = -1. I was having trouble figuring that out an hour ago, I knew that it was related to the 3-check, but was having a devil of a time working it out (lack of coffee). \$\endgroup\$ – Draco18s Jul 12 '17 at 14:58
4
\$\begingroup\$

Mathematica, 133 bytes

(s={};c=Characters;j=c@#;Table[If[FreeQ[j~Count~#&/@c[#[[i]]]&@ToUpperCase@IntegerName@Range@9,0],s~AppendTo~i],{i,9}];FromDigits@s)&


input

"VENESGTHIEENNI"

output

789

\$\endgroup\$
  • \$\begingroup\$ Could you save an extra byte with c@#[[i]] instead of c[#[[i]]]? You might be able to save another byte by using infix syntax ~ on the Table. \$\endgroup\$ – numbermaniac Jul 12 '17 at 6:49
4
\$\begingroup\$

C#, 218 bytes

Short Version:

string q(string s){var n="ONE,TWO,THREE,FOUR,FIVE,SIX,SEVEN,EIGHT,NINE".Split(',');for(inti=0,j;;i++)for(j=0;n[i].IndexOf(s[j])>=0;){if(++j==n[i].Length){var r=++i+"";for(;j<s.Length;r+=++i)j+=n[i].Length;return r;}}}

Expanded version:

string q(string s)
{
    var n = "ONE,TWO,THREE,FOUR,FIVE,SIX,SEVEN,EIGHT,NINE".Split(',');
    for (int i = 0, j; ; i++)
        for (j = 0; n[i].IndexOf(s[j]) >= 0;)
        {
            if (++j == n[i].Length)
            {
                var r = ++i + "";
                for (; j < s.Length; r += ++i)
                    j += n[i].Length;
                return r;
            }
        }
}

Try ONLINE!

Being my first entry, I'm uncertain about the rules... I'm only counting the size of the class used to de-crypt, not the code that tests it, right?

Edit

And for the fun of it - here's what I started doing, not reading the complete rules :S - See it at IdeOne. It de-crypts even when characters from one digit can be scrambled to any place in the string.

Edit 2

Shortened according to tips by TheLethalCoder. Thanks!

Edit 3

And now Titus shaved of a few more bytes. Thanks!

\$\endgroup\$
  • 2
    \$\begingroup\$ Hello and welcome to PPCG! You only need to include the method, you can remove public static from it to. You can convert to an anonymous method like s=>{<do stuff>return"";}. You can use var a few times, declaring variables together saves bytes i.e. int i=1,j;. Creating an array from a string and splitting on it is usually shorter (though I haven't checked in this case) i.e. "ONE|TWO".Split('|'). You can use <0 instead of ==-1 \$\endgroup\$ – TheLethalCoder Jul 11 '17 at 11:00
  • \$\begingroup\$ For more tips see Tips for code-golfing in C#. \$\endgroup\$ – TheLethalCoder Jul 11 '17 at 11:01
  • \$\begingroup\$ @TheLethalCoder Great tips, thanks! \$\endgroup\$ – SamWhan Jul 11 '17 at 11:03
  • \$\begingroup\$ Not tested at all but I believe the following is the equivalent of your code for 221 bytes: s=>{var n="ONE|TWO|THREE|FOUR|FIVE|SIX|SEVEN|EIGHT|NINE".Split('|');for(int i=0,j;++i<= 9;)for(j=0;n[i-1].IndexOf(s[j])<0;){if(++j==n[i-1].Length){var r=i+"";while(j<s.Length){j+=n[i].Length;r+=++i;}return r;}}return "";} \$\endgroup\$ – TheLethalCoder Jul 11 '17 at 11:07
  • \$\begingroup\$ On a side note it is usually easier to use TIO for your TIO's! \$\endgroup\$ – TheLethalCoder Jul 11 '17 at 11:10
3
\$\begingroup\$

JavaScript (ES6), 142 139 Bytes

Saved 3 Bytes thanks to Neil.

Doesn't currently take advantage of numbers are always arranged in ascending order

f=s=>s?'ENO|OTW|EEHRT|FORU|EFIV|ISX|EENSV|EGHIT|EINN'.split`|`.findIndex(w=>[...s.slice(0,y=w.length)].sort().join``==w)+1+f(s.slice(y)):''

f=s=>s?'ENO|OTW|EEHRT|FORU|EFIV|ISX|EENSV|EGHIT|EINN'.split`|`.findIndex(w=>[...s.slice(0,y=w.length)].sort().join``==w)+1+f(s.slice(y)):''

const testCases = ['NEO', 'ENOWOT', 'EONOTWHTERE', 'SNVEEGHEITNEIN', 'ENOOWTEERHTRUOFEVIFXISNEVESTHGIEENIN']

testCases.forEach(testCase => console.log(testCase, f(testCase)))

\$\endgroup\$
  • \$\begingroup\$ wait what?? "axbxc".split`x`.join``. How is this called? Can't seem to find anything on google. \$\endgroup\$ – Qwerty Jul 11 '17 at 10:31
  • \$\begingroup\$ @Qwerty - They are tagged template literals, an ES6 feature which I'm using to save a few bytes by not needing parens in the case of split and join \$\endgroup\$ – Craig Ayre Jul 11 '17 at 10:33
  • \$\begingroup\$ You answered it. I know tagged template literals, but I haven't realised you can use it on these functions as well. Thank you. \$\endgroup\$ – Qwerty Jul 11 '17 at 10:35
  • \$\begingroup\$ They're a little different, you have template literals (e.g. x=`foo${5+5}bar`), they're tagged when you call a function using them without parens: foo`foo${5+5}bar` which is the same as foo(['foo','bar'], 10) \$\endgroup\$ – Craig Ayre Jul 11 '17 at 10:38
  • 1
    \$\begingroup\$ f(s.slice(y)) is always a string so you don't need the ''+ before it. \$\endgroup\$ – Neil Jul 11 '17 at 11:51
2
\$\begingroup\$

Jelly, 38 bytes

Dị“©ȯ¿w¶&ÇhṆỌƘ#Ȯʋ~¢CNẓ_»Ḳ¤FṢŒu
L3*Ç€iṢ

Try it online!

Explanation

L3*Ç€iṢ    Main link. Argument: s (string)
L            Get length of s.
 3*          Raise 3 to that power. This will always be greater than n.
   ǀ        Get the name of each of the numbers using the helper link.
     iṢ      Find the position of the sorted input.

Dị“©ȯ¿w¶&ÇhṆỌƘ#Ȯʋ~¢CNẓ_»Ḳ¤FṢŒu    Helper link. Argument: n (number)
D                                   Get digits of n.
  “©ȯ¿w¶&ÇhṆỌƘ#Ȯʋ~¢CNẓ_»            The string "one two (...) eight nine AA".
                        Ḳ           Split that string at spaces.
 ị                                  Get name of each digit in the list.
                          F         Flatten to a single string.
                           Ṣ        Sort the characters.
                            Œu      Make uppercase.
\$\endgroup\$
2
\$\begingroup\$

Javascript (ES6), 221 bytes

s=>(m=btoa`8Ñ>Mc¾LtDáNQ!Q>HþHA7átþ4Ò`.split`+`.map(s=>RegExp(s.replace(/(.)\1*/g,c=>`(?=(.*${c[0]}){${c.length}})`))),t=0,r=0,[...s].map(c=>(t+=c,d=1,n=0,m.map((r,i)=>t.match(r)&&(d--,n=i)),d||(r=r*10+n+1,t=0))),r)

Example code snippet:

f=

s=>(m=btoa`8Ñ>Mc¾LtDáNQ…!Q>H…þHA7átþ4Ò`.split`+`.map(s=>RegExp(s.replace(/(.)\1*/g,c=>`(?=(.*${c[0]}){${c.length}})`))),t=0,r=0,[...s].map(c=>(t+=c,d=1,n=0,m.map((r,i)=>t.match(r)&&(d--,n=i)),d||(r=r*10+n+1,t=0))),r)

console.log(f("NEO"))
console.log(f("ENOWOT"))
console.log(f("EONOTWHTERE"))
console.log(f("SNVEEGHEITNEIN"))
console.log(f("ENOOWTEERHTRUOFEVIFXISNEVESTHGIEENIN"))

\$\endgroup\$
2
\$\begingroup\$

Retina, 160 bytes

([ONE]{3})*([TWO]{3})*([THRE]{5})*([FOUR]{4})*([FIVE]{4})*([SIX]{3})*([SEVN]{5})*([EIGHT]{5})*([NIE]{4})*
$#1$*1$#2$*2$#3$*3$#4$*4$#5$*5$#6$*6$#7$*7$#8$*8$#9$*9

Try it online! Loosely based on @TessellatingHeckler's PowerShell answer.

\$\endgroup\$
2
\$\begingroup\$

Retina, 88 bytes

[EFIST]

^(ON|NO)*
$#1$*1
O

W
2
HR|RH
3
UR|RU
4
X
6
GH|HG
8
(NN)*$
$#1$*9
r`NV|VN
7
V
5

Try it online!

Explanation

  • First, drop a bunch of unnecessary characters not needed for distinctness
  • Pick the 1s off the front (this lets us drop the rest of the Os immediately after and clears up some Ns before we get to the 5, 7, 9 mess)
  • 2, 3, 4, 6, and 8 are now trivial
  • 9s are a double NN, so grab those off the end before we deal with 5 and 7
  • Replace 7s from the right (so we don't reduce VNV to 75 instead of 57)
  • 5s are the remaining Vs
\$\endgroup\$
  • \$\begingroup\$ If you add %(G` to the header, you can use the original code and it will evaluate each line of the input separately: TIO \$\endgroup\$ – PunPun1000 Aug 11 '17 at 19:57
  • \$\begingroup\$ Thanks @PunPun1000. I figured there must be a way to do that but gave up after not finding it quickly. \$\endgroup\$ – Kytheron Aug 18 '17 at 1:09
1
\$\begingroup\$

PowerShell, 182 bytes

[regex]::Replace("$args",'(?<1>[ONE]{3z2>[TWO]{3z3>[THRE]{5z4>[FOUR]{4z5>[FIVE]{4z6>[SIX]{3z7>[SVEN]{5z8>[EIGHT]{5z9>[NIE]{4})'.replace('z','})|(?<'),{$args.groups.captures[1].name})

Try it online!

Ungolfed but not working code:

[System.Text.RegularExpressions.Regex]::Replace("$args",

    '(?<1>[ONE]{3})       
    |(?<2>[TWO]{3})
    |(?<3>[THRE]{5})
    |(?<4>[FOUR]{4})
    |(?<5>[FIVE]{4})
    |(?<6>[SIX]{3})
    |(?<7>[SVEN]{5})
    |(?<8>[EIGHT]{5})
    |(?<9>[NIE]{4})'

    ,{$args.groups.captures[1].name}
)

e.g. (?<3>[THRE]{5}) matches the character class THRE, so it can match them out of order, and has to match any of these characters five times next to each other, and the capture group is named '3' to map names with numbers.

Rudimentary compression by swapping the repeating text })|(?< for a z.

\$\endgroup\$
1
\$\begingroup\$

C++, 296, 288 bytes

Short Version:

#define T string
using namespace std;T N[]={"ONE","TWO","THREE","FOUR","FIVE","SIX","SEVEN","EIGHT","NINE"};T Q(T S){T R="";for(int i=0;i<9;i++){do{if(S.find(N[i])!=T::npos){S.erase(S.find(N[i]),N[i].size());R+=to_string(i+1);}}while(next_permutation(N[i].begin(),N[i].end()));}return R;}

Full Version:

#define T string
using namespace std;

T N[]={"ONE","TWO","THREE","FOUR","FIVE","SIX","SEVEN","EIGHT","NINE"};

T Q(T S)
{
    T R="";
    for(int i=0;i<9;i++)                             //for all possible                             
                                                     //codewords (ONE,TWO...NINE)   
    {
        do
        {   
            if(S.find(N[i])!=T::npos)                //if found in encrypted word
            {
                S.erase(S.find(N[i]),N[i].size());  //erase it from the word
                R+=to_string(i+1);                  //save integer to the result string
            }
                                                    //check next permuation of codeword  

        } while(next_permutation(N[i].begin(),N[i].end())); 
    }                                                   

    return R;
}

Try ONLINE!

Edit:
1) 200->296 bytes, for including namespace and definition of N in the count, as suggested by orlp 2) 296->288, for using macro, thanks to Zacharý

\$\endgroup\$
  • \$\begingroup\$ You need to include the definition of N and using namespace std; into your byte count. \$\endgroup\$ – orlp Jul 11 '17 at 9:19
  • \$\begingroup\$ I should be more specific, not just include it in your byte count but also into your answer. Your answer must be able to run just by calling Q right after it without any other additions. \$\endgroup\$ – orlp Jul 11 '17 at 9:39
  • \$\begingroup\$ I re-edited to include it all. For the definition of N i was not sure myself, but for the namespace, I usually dont include it (treat it as library stuff). Though, in the current code it is crucial for the string to work \$\endgroup\$ – koita_pisw_sou Jul 11 '17 at 10:07
  • 1
    \$\begingroup\$ Can you define a macro to save a few bytes? repl.it/JY7k \$\endgroup\$ – Zacharý Jul 12 '17 at 13:30
1
\$\begingroup\$

Ruby, 138 114 110 bytes

gsub(/#{"3ONE3TWO5THRE4FOUR4FIVE3SIX5SEVN5EIGHT4NIE".gsub(/(.)(\D+)/,'([\2]{\1})|')}/){(1..9).find{|i|$~[i]}}

Byte count includes 1 byte for the -p option.

What?

This:

/#{"3ONE3TWO5THRE4FOUR4FIVE3SIX5SEVN5EIGHT4NIE".gsub(/(.)(\D+)/,'([\2]{\1})|')}/

is a regex literal which, through string interpolation, evaluates to:

/([ONE]{3})|([TWO]{3})|([THRE]{5})|([FOUR]{4})|([FIVE]{4})|([SIX]{3})|([SEVN]{5})|([EIGHT]{5})|([NIE]{4})|/

If we assign that to regex, the rest of the code is somewhat easy to grasp: Each match in the input is substituted with the number of the capturing group, extracted from the magical variable $~ which contains the current match data:

gsub(regex){(1..9).find{|i|$~[i]}}

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Java 8, 198 256 bytes

s->{String r="",x=r;for(String n:"ONE TWO THREE FOUR FIVE SIX SEVEN EIGHT NINE".split(" ")){for(char c:n.toCharArray())x+="(?=.*"+c+")";x+="["+n+"]{"+n.length()+"}x";}for(int i=0,q;i<9;)for(q=(s+" ").split(x.split("x")[i++]).length-1;q-->0;)r+=i;return r;}

+58 bytes.. due to regex of the previous version not working properly (it was also matching "EEE";"EEN";etc.)

Explanation:

Try it here.

s->{                     // Method with String as parameter and return-type
  String r="",           //  Result-String
         x=r;            //  Regex-String
  for(String n:"ONE TWO THREE FOUR FIVE SIX SEVEN EIGHT NINE".split(" ")){
                         //  Loop (1) from "ONE" through "NINE":
    for(char c:n.toCharArray())
                         //   Inner loop (2) over the characters of this String
      x+="(?=.*"+c+")";  //    Append regex-group `(?=\w*c)` where `c` is the capital character
                         //   End of inner loop (2) (implicit / single-line body)
    x+="["+n+"]{"+n.length()+"}x";
                         //   Append regex part `[s]{n}` where `s` is the String, and `n` is the length
  }                      //  End of loop (1)
  // The regex now looks like this, which we can split on "x":
  // (?=.*O)(?=.*N)(?=.*E)[ONE]{3}x(?=.*T)(?=.*W)(?=.*O)[TWO]{3}x(?=.*T)(?=.*H)(?=.*R)(?=.*E)(?=.*E)[THREE]{5}x(?=.*F)(?=.*O)(?=.*U)(?=.*R)[FOUR]{4}x(?=.*F)(?=.*I)(?=.*V)(?=.*E)[FIVE]{4}x(?=.*S)(?=.*I)(?=.*X)[SIX]{3}x(?=.*S)(?=.*E)(?=.*V)(?=.*E)(?=.*N)[SEVEN]{5}x(?=.*E)(?=.*I)(?=.*G)(?=.*H)(?=.*T)[EIGHT]{5}x(?=.*N)(?=.*I)(?=.*N)(?=.*E)[NINE]{4}x
  for(int i=0,q;i<9;)    //  Loop (3) from 0 through 9 (exclusive)
    for(q=(s+" ").split(x.split("x")[i++]).length-1;
                         //   Split the input on the current regex-part,
                         //   and save the length - 1 in `q`
        q-->0;           //   Inner loop (4) over `q`
      r+=i               //    And append the result-String with the current index (+1)
    );                   //   End of inner loop (4)
                         //  End of loop (3) (implicit / single-line body)
  return r;              //  Return the result-String
}                        // End of method
\$\endgroup\$
  • 1
    \$\begingroup\$ Erf... wrong result for "ENOOWTEERHTRUOFEVIFXISNEVESTHGIEENIN" :( \$\endgroup\$ – Olivier Grégoire Jul 12 '17 at 10:28
  • \$\begingroup\$ Yeah, that's the only thing that prevented me to +1 this! My solution was 240 bytes... before you beat me to it. \$\endgroup\$ – Olivier Grégoire Jul 12 '17 at 13:22
  • \$\begingroup\$ @OlivierGrégoire Feel free to post your 240 byte solution, because I'm unable to find a solution.. The disadvantage about [ONE]{3} is that it also matches EEN at the end of that test case with parts of EIGHT and NINE.. And I doubt there is a regex to match all these: ENO|EON|NEO|NOE|OEN|ONE without also matching EEE;EEN;EEO;... for all numbers that is shorter than 40 bytes.. Maybe I can do something using substring and reverse checking the numbers, but I don't really have the time to figure it out now.. \$\endgroup\$ – Kevin Cruijssen Jul 12 '17 at 14:17
  • \$\begingroup\$ @OlivierGrégoire If you still have your 240 byte answer, feel free to post it. Just came across this challenge again, and fixed my answer by making a new regex for +58 bytes.. \$\endgroup\$ – Kevin Cruijssen Aug 11 '17 at 12:27
  • 1
    \$\begingroup\$ Well, looks like I found an even shorter way while redoing this challenge :p \$\endgroup\$ – Olivier Grégoire Aug 11 '17 at 13:30
1
\$\begingroup\$

Java (OpenJDK 8), 181 bytes

s->{String x="",r;for(int i=0,l;i<9;)for(r="ONE,TWO,THREE,FOUR,FIVE,SIX,SEVEN,EIGHT,NINE".split(",")[i++],l=r.length();s.matches("["+r+"]{"+l+"}.*");s=s.substring(l))x+=i;return x;}

Try it online!

I took the liberty to reuse Kevin Cruyssen's TIO template. Hope you don't mind ;)

\$\endgroup\$
  • \$\begingroup\$ Ah, nevermind my previous comment.. You build the regex, instead of loop over the regex. Still, I was close with my first answer if only I had used the s.substring. The worst part is, is that I am using s.substring in my current answer, lol.. Ah well, +1 from me. Glad it's almost weekend.. \$\endgroup\$ – Kevin Cruijssen Aug 11 '17 at 13:57
1
\$\begingroup\$

05AB1E, 36 31 bytes

‘€µ‚•„í†ìˆÈŒšï¿Ÿ¯¥Š‘#vyœN>UvyX:

Try it online!


View it ran with debug: TIO With Debug

‘€µ‚•„í†ìˆÈŒšï¿Ÿ¯¥Š‘# | Push ['ONE', 'TWO', 'THREE', 'FOUR', 'FIVE', 'SIX', 'SEVEN', 'EIGHT', 'NINE']
vyœ                   | For each list of permutations of that word...
   N>U                | Push index + 1 into register X.          
      vyX:            | Replace each permutation with X.
\$\endgroup\$
  • \$\begingroup\$ I was just suggesting you had the green mark rather than me and I noticed a bug: FURONESEV returns FUR1SEV :( \$\endgroup\$ – Jonathan Allan Feb 19 '18 at 13:51
1
\$\begingroup\$

Perl 5, 102 + 1 (-n) = 103 bytes

for$i(map{"[$_]{".length.'}'}ONE,TWO,THREE,FOUR,FIVE,SIX,SEVEN,EIGHT,NINE){$,++;print$,while(s/^$i//)}

Try it online!

\$\endgroup\$
  • \$\begingroup\$ Nice! Couple of tricks that help: map{...} can often be replaced with map...,, length and y///c are usually interchangeable too (not always smaller when not working on $_ though!), instead of the while, ++$,x s/^$i// is shorter, and if you change -n to -p you can append to ` $\ ` instead of calling print! Try it online! \$\endgroup\$ – Dom Hastings Aug 18 '17 at 12:06
  • \$\begingroup\$ Also, I hope you don't mind me posting any advice, if you'd prefer I'll refrain. :) \$\endgroup\$ – Dom Hastings Aug 18 '17 at 12:08
0
\$\begingroup\$

Python 3, 238 236 bytes

def f(s):
 e=''
 while len(s):
  for i in range(9):
   for r in[''.join(p)for p in permutations('ONE TWO THREE FOUR FIVE SIX SEVEN EIGHT NINE'.split()[i])]: 
    if s[:len(r)]==r:e+=str(i+1);s=s[len(r):]
 return e
from itertools import*

Try it online!


Brute-force solution, doesn't take advantage non-decreasingness of digits.


Thanks to @Mr. Xcoder for saving 2 bytes!

\$\endgroup\$
  • \$\begingroup\$ You have to include def f(s): in your byte count, this is not an anonymouos function \$\endgroup\$ – Mr. Xcoder Jul 11 '17 at 21:40
  • \$\begingroup\$ Also you can replace while len(s)>0 with while len(s) \$\endgroup\$ – Mr. Xcoder Jul 11 '17 at 21:41
  • \$\begingroup\$ @Mr.Xcoder thanks for that clarification \$\endgroup\$ – Chase Vogeli Jul 12 '17 at 3:12
  • \$\begingroup\$ You can move the declaration of e into the function header for -1 byte. Also, exec and list comprehensions might save bytes on indentation. \$\endgroup\$ – CalculatorFeline Jul 13 '17 at 1:25
0
\$\begingroup\$

PHP, 141 bytes

for($a=count_chars($argn);$c=ord($s[++$p]?:$s=[OWU,W,HG,U,FU,X,SX,G,N17.$p=0][$i-print str_repeat($i++,$x)]);)$x=$a[$i+48]+=($p?-1:1)*$a[$c];

older version, 151 bytes:

for($a=count_chars($argn,1);$s=[OWU,W,HG,U,FU,X,SX,G,N17][+$i++];print str_repeat($i,$a[$i+48]))for($p=0;$c=ord($s[$p]);)$a[$i+48]+=($p++?-1:1)*$a[$c];

loops through the digits from 1 to 9, counting unique characters in the word and subtracting non-unique characters´ counts, printing the digit on the go.
Although it is printing on the go, the digit counts must be stored for the 9 case to work.

Run as pipe with -nR or try it online.

It would save 4 more bytes to store the digit counts in $a[$i] instead of $a[$i+48] and use ASCII 1 and 7 (in quotes) instead of the digit characters themselves.

breakdown

for(
    $a=count_chars($argn,1);                # count character occurences in input
    $s=[OWU,W,HG,U,FU,X,SX,G,N17][+$i++];   # loop through digit names
    print str_repeat($i,$a[$i+48])              # print digit repeatedly
)
    for($p=0;$c=ord($s[$p]);)                   # loop through name
        $a[$i+48]+=                                 # add to digit count
        ($p++?-1:1)*                                # (add first, subtract other)
        $a[$c];                                     # character occurences

ONE is not the only word with an O, so it needs to subtract the counts for W (only appearing in TWO) and U (only appearing in FOUR) and so on.
NINE is special, because there is no way to just subtract if I used the letters (that would require I-X-G-F+U or N-O-S+W+U+X), so I use the digit counts instead.

PHP, 160 bytes

$a=count_chars($argn);foreach([W2O,U4FOR,X6SI,G8I,F5I,O1,R3,S7,I9]as$s)for(${$s[$p=1]}+=$n=$a[ord($s)];$c=ord($s[++$p]);)$a[$c]-=$n;while($$i--?print$i:$i++<9);

assumes all upper case input; characters may be scrambled all over.
Run as pipe with -nR or try it online.

explanation

loops through the digit words, counting their unique characters´ occurences in the input and in the process reducing the count of other characters. "Other characters" could mean all other characters in the word; but only considering those that will be needed later saved 19 bytes.

Transforming the str_repeat loop to a combined loop saved 5 bytes.

And using variable variables for the digit count saved another 8.

breakdown

$a=count_chars($argn);                              # count character occurences in input
foreach([W2O,U4FOR,X6SI,G8I,F5I,O1,R3,S7,I9]as$s)   # loop through digit names
    for(${$s[$p=1]}+=                                   # 2. add to digits count
        $n=$a[ord($s)];                                 # 1. get count of unique character
        $c=ord($s[++$p]);)                              # 3. loop through other characters
        $a[$c]-=$n;                                         # reduce character count
while(
    $$i--?print$i                                       # print digit repeatedly
    :$i++<9);                                       # loop through digits
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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