The Original Number

36

1

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

aimorris

Posted 2017-07-11T07:20:37.273

Reputation: 1 217

5In all the test cases, only the letters within a word are shuffled, not letters between words. Will that always be the case? – xnor – 2017-07-11T07:32:26.220

1@xnor That will always be the case. I have edited the question. – aimorris – 2017-07-11T07:39:10.517

1then you need to change this "....(ie. "ONETWOTHREE" -> "TTONWOHREEE")" – J42161217 – 2017-07-11T07:41:56.013

@Jenny_mathy I have edited the question. Thanks. – aimorris – 2017-07-11T07:43:11.597

@ComradeSparklePony I edited the question to make it easier. – aimorris – 2017-07-11T07:50:44.063

@ComradeSparklePony "The numbers will only be from 1 to 9 inclusive" – Mr. Xcoder – 2017-07-11T07:58:46.807

@Mr.Xcoder Oops, I was looking at a cached copy. It is all set now. :) – Comrade SparklePony – 2017-07-11T08:05:16.797

What does "non strict" mean in "his number is always a non strict increasing sequence"? – TessellatingHeckler – 2017-07-11T08:26:17.820

Can you add even more complicated test cases? – J42161217 – 2017-07-11T08:29:51.550

2@ 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) – koita_pisw_sou – 2017-07-11T08:37:47.313

1Technically speaking, this is an encoding, not encryption, since there is no key. – Patrick Roberts – 2017-07-11T09:10:29.610

@PatrickRoberts Thanks for that. :P – aimorris – 2017-07-11T09:14:55.560

You should include at least one example with digits appearing more than once. – Titus – 2017-07-11T14:56:22.160

@MagicOctopusUrn That number isn't a non-decreasing sequence. Therefore it is not a valid input – PunPun1000 – 2017-08-11T19:52:34.823

@PunPun1000 ahhh! Yes! Okay, I wondered why the spec was so loose; was just me being dumb. We do have ONE answer that actually works on it though. – Magic Octopus Urn – 2017-08-11T19:57:08.617

It would be interesting to have a version of this challenge where it is allowed to shuffle letters throughout the entire string. – Cristian Lupascu – 2017-08-18T06:20:21.167

Answers

5

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

Jonathan Allan

Posted 2017-07-11T07:20:37.273

Reputation: 67 804

Your answer is literally the only answer on this page that returns a correct value for: NINEONENIENOENNNIENOENNEINEONEINEONNENIENOINNEINENINNEINENIENNIENNNNIENNEININENIENNENINEINENINENNIEINNEINNENNIENIN. – Magic Octopus Urn – 2017-08-11T14:53:55.107

+Infinity points. – Magic Octopus Urn – 2017-08-11T14:56:08.650

Thanks! (that threw me because there are zero width spaces to the code block in the comment, but (whew) it does work)

– Jonathan Allan – 2017-08-11T15:14:08.893

It's not a valid input anyway ;). – Magic Octopus Urn – 2017-08-14T01:53:15.843

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. – Jonathan Allan – 2017-08-14T02:11:28.777

@Amorris - if you are accepting with the green check mark I believe it should rightly go to Magic Octopus' answer, although of course as the question asker it is your prerogative to choose.

– Jonathan Allan – 2018-02-19T13:40:59.317

@Amorris ...although having said that I spotted a bug in that entry :( – Jonathan Allan – 2018-02-19T13:52:05.300

10

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.

Score_Under

Posted 2017-07-11T07:20:37.273

Reputation: 271

What's Py3K?... – CalculatorFeline – 2017-07-11T16:18:54.750

Apologies, edited (it's the former name of python 3) – Score_Under – 2017-07-11T16:19:47.087

1It'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. [https://tio.run/##NU7NCoJAGDy7T/ElhK5KmHooY49SXiQoOpSBoru5l1U@FQx6d1uDLgPzw8x076FpVTDPNRfwsnuvZL4nmGXRmIBoESqQCvqYGKXLwshxWqztiq6j8IDMOq/yaReOGniaT/vgc7c2QiqdaNAuKXW3xJACMBa6V7gFFp5PAPkwogIx//rLni8TUnXjYOtVo0OpBjBzlSvT@/nEQN4D0/8WRv8RLc4P85ZkyeV4PaVJkmWp@fwC Try it online!] – Coty Johnathan Saxman – 2017-07-12T07:15:35.587

Also, you might be able to save 6 bytes using literal \x83, \x8e, and \x92 in the string. – CalculatorFeline – 2017-07-12T15:36:52.660

@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. – Score_Under – 2017-07-12T21:54:30.930

112 bytes as a full program in either Python 2 or Python 3

– Jonathan Allan – 2017-08-14T20:22:49.640

6

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.

mdahmoune

Posted 2017-07-11T07:20:37.273

Reputation: 2 605

What an interesting use of vars! – xnor – 2017-07-11T23:24:09.433

@xnor it was ovs how learned me that for other golfs :))) – mdahmoune – 2017-07-12T11:48:09.447

Very clever. Have a +1 for adapting my answer (as flawed as it was originally). – Draco18s no longer trusts SE – 2017-07-12T15:44:51.713

5

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!

Jörg Hülsermann

Posted 2017-07-11T07:20:37.273

Reputation: 13 026

fails for ENOOWTWTOWOT – Titus – 2017-07-11T14:58:16.620

@Titus is now fixed. I have misunderstand the question – Jörg Hülsermann – 2017-07-11T17:05:31.643

Yea the examples are somewhat misleading. Wow that did cost! Would you break that down?! – Titus – 2017-07-11T17:35:56.963

@Titus I think I have reach the limit to find another way as your approach – Jörg Hülsermann – 2017-07-11T18:18:17.257

1$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.) – Titus – 2017-07-11T19:09:07.380

Why all the indexing? Watch these 165 bytes: for($c=count_chars($argn);$i++<9;)echo str_repeat($i,[0,$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-$g-$f][$i]); – Titus – 2017-07-11T19:24:55.967

@Titus not enough thinking of it – Jörg Hülsermann – 2017-07-11T21:17:31.440

How come your answer has 3 votes and mine has none although you have no explanation at all? Smells funny. – Titus – 2017-07-12T09:01:47.250

@Titus I think it is a give and take like in life You have vote 238 times other questions or answers. The votes are not important in my opinion. A long solution is better as no solution. And if somebody ask for an explanation I can add it. The sense of PPCG is vote for good answers/questions and not reach the most votes. Sometimes I reach a nice answer bagde and I am happy about it. But I think I will never reach the next step. – Jörg Hülsermann – 2017-07-12T10:00:01.143

5

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)))

Draco18s no longer trusts SE

Posted 2017-07-11T07:20:37.273

Reputation: 3 053

1Great, but there is a problem with counting 9... I think it may be i-x-g-f+u – mdahmoune – 2017-07-11T22:43:57.770

@mdahmoune Shoot, you're right. I messed that one up. :< – Draco18s no longer trusts SE – 2017-07-12T01:49:24.200

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``. – Neil – 2017-07-12T12:24:21.293

@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 – Draco18s no longer trusts SE – 2017-07-12T13:33:53.937

@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). – Draco18s no longer trusts SE – 2017-07-12T14:58:23.317

@mdahmoune I hadn't last night as I hadn't the time to do so, I was only able to make the comment. The answer has been updated now though. :) – Draco18s no longer trusts SE – 2017-07-12T15:24:13.713

Ah, that's the problem - the 3s are given by R - U, not R - W. Given that, you use U enough times to make it worth while computing in advance: 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`` – Neil – 2017-07-12T15:30:42.233

In fact @mdahmoune already corrected that one too in his version... – Neil – 2017-07-12T15:33:15.693

@Neil Can I blame coffee or the lack thereof? I'm not sure. I swear I double-checked them all. @..@ Nine I know I checked three times and still messed that up. Thanks. – Draco18s no longer trusts SE – 2017-07-12T15:35:48.160

4

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

J42161217

Posted 2017-07-11T07:20:37.273

Reputation: 15 931

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. – numbermaniac – 2017-07-12T06:49:24.387

4

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!

SamWhan

Posted 2017-07-11T07:20:37.273

Reputation: 141

2Hello 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 – TheLethalCoder – 2017-07-11T11:00:24.913

For more [tag:tips] see Tips for code-golfing in C#.

– TheLethalCoder – 2017-07-11T11:01:11.337

@TheLethalCoder Great tips, thanks! – SamWhan – 2017-07-11T11:03:43.227

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 "";} – TheLethalCoder – 2017-07-11T11:07:02.683

On a side note it is usually easier to use TIO for your TIO's!

– TheLethalCoder – 2017-07-11T11:10:47.050

Your code appears to be 259 bytes. – CalculatorFeline – 2017-07-11T16:19:46.063

Welcome! Try for(j=0;n[i].IndexOf(s[j])>=0;) (or similar; your short code doesn´t seem to match the expanded version), saving 5 bytes. If that also saves braces, so will for(;j<s.Length;r+=++i;)j+=n[i].Length;. – Titus – 2017-07-11T19:39:31.170

On a side note, if the code in your first edit is shorter than the code you provided, you should use that one. It will work even when it is only scrambled throughout the word, too. – aimorris – 2017-07-11T20:17:19.123

@CalculatorFeline Really? How is that? – SamWhan – 2017-07-12T07:24:47.243

@Titus Thanks! Great tips. (It strange when you get something pointed out how obvious it sometimes seems. :) – SamWhan – 2017-07-12T07:26:40.967

On Revision 5 you forgot to update the code. – CalculatorFeline – 2017-07-12T15:35:44.943

@CalculatorFeline Ahh... I see. Sorry! – SamWhan – 2017-07-13T06:55:31.737

This outputs 123 for NOENEIN. – Magic Octopus Urn – 2017-08-11T19:58:52.537

3

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)))

Craig Ayre

Posted 2017-07-11T07:20:37.273

Reputation: 217

wait what?? "axbxc".split\x`.join```. How is this called? Can't seem to find anything on google. – Qwerty – 2017-07-11T10:31:04.453

@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

– Craig Ayre – 2017-07-11T10:33:43.367

You answered it. I know tagged template literals, but I haven't realised you can use it on these functions as well. Thank you. – Qwerty – 2017-07-11T10:35:34.510

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) – Craig Ayre – 2017-07-11T10:38:54.897

1f(s.slice(y)) is always a string so you don't need the ''+ before it. – Neil – 2017-07-11T11:51:09.863

2

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.

PurkkaKoodari

Posted 2017-07-11T07:20:37.273

Reputation: 16 699

There is an issue with your code. Try passing the string "EIGHTNINE" into it :) – aimorris – 2017-07-11T08:09:08.403

@Amorris fixed for 0 bytes. – PurkkaKoodari – 2017-07-11T08:16:08.067

I think it doesn't work for "VENESGTHIEENNI" – J42161217 – 2017-07-11T08:26:54.307

I second @Jenny_mathy – aimorris – 2017-07-11T08:30:26.027

@Jenny_mathy The program is very inefficient and runs out of time and memory for long inputs (I know, it's really bad). You can replace the 3 with 2.2 to use a smaller upper bound, which allows you to easily calculate 789 without changing the working principle. 2 would be nice, but it would barely fail for certain inputs with lots of sixes.

– PurkkaKoodari – 2017-07-11T10:37:52.440

2

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"))

Herman L

Posted 2017-07-11T07:20:37.273

Reputation: 3 611

2

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.

Neil

Posted 2017-07-11T07:20:37.273

Reputation: 95 035

2

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

Kytheron

Posted 2017-07-11T07:20:37.273

Reputation: 21

If you add %(G` to the header, you can use the original code and it will evaluate each line of the input separately: TIO

– PunPun1000 – 2017-08-11T19:57:39.977

Thanks @PunPun1000. I figured there must be a way to do that but gave up after not finding it quickly. – Kytheron – 2017-08-18T01:09:41.697

1

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ý

koita_pisw_sou

Posted 2017-07-11T07:20:37.273

Reputation: 161

You need to include the definition of N and using namespace std; into your byte count. – orlp – 2017-07-11T09:19:47.620

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. – orlp – 2017-07-11T09:39:59.593

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 – koita_pisw_sou – 2017-07-11T10:07:44.457

1

Can you define a macro to save a few bytes? https://repl.it/JY7k

– Zacharý – 2017-07-12T13:30:01.120

1

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.

TessellatingHeckler

Posted 2017-07-11T07:20:37.273

Reputation: 2 412

1

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!

daniero

Posted 2017-07-11T07:20:37.273

Reputation: 17 193

1

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

Kevin Cruijssen

Posted 2017-07-11T07:20:37.273

Reputation: 67 575

1Erf... wrong result for "ENOOWTEERHTRUOFEVIFXISNEVESTHGIEENIN" :( – Olivier Grégoire – 2017-07-12T10:28:56.593

Yeah, that's the only thing that prevented me to +1 this! My solution was 240 bytes... before you beat me to it. – Olivier Grégoire – 2017-07-12T13:22:46.780

@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.. – Kevin Cruijssen – 2017-07-12T14:17:26.687

@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.. – Kevin Cruijssen – 2017-08-11T12:27:28.270

Nope, I deleted it. I usually delete my entries when I posted them. – Olivier Grégoire – 2017-08-11T13:01:54.967

1

Well, looks like I found an even shorter way while redoing this challenge :p

– Olivier Grégoire – 2017-08-11T13:30:27.010

1

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 ;)

Olivier Grégoire

Posted 2017-07-11T07:20:37.273

Reputation: 10 647

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.. – Kevin Cruijssen – 2017-08-11T13:57:39.910

1

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.

Magic Octopus Urn

Posted 2017-07-11T07:20:37.273

Reputation: 19 422

I was just suggesting you had the green mark rather than me and I noticed a bug: FURONESEV returns FUR1SEV :( – Jonathan Allan – 2018-02-19T13:51:32.440

1

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!

Xcali

Posted 2017-07-11T07:20:37.273

Reputation: 7 671

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!

– Dom Hastings – 2017-08-18T12:06:02.600

Also, I hope you don't mind me posting any advice, if you'd prefer I'll refrain. :) – Dom Hastings – 2017-08-18T12:08:03.140

0

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!

Chase Vogeli

Posted 2017-07-11T07:20:37.273

Reputation: 141

You have to include def f(s): in your byte count, this is not an anonymouos function – Mr. Xcoder – 2017-07-11T21:40:01.197

Also you can replace while len(s)>0 with while len(s) – Mr. Xcoder – 2017-07-11T21:41:28.693

@Mr.Xcoder thanks for that clarification – Chase Vogeli – 2017-07-12T03:12:15.567

You can move the declaration of e into the function header for -1 byte. Also, exec and list comprehensions might save bytes on indentation. – CalculatorFeline – 2017-07-13T01:25:33.863

0

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

Titus

Posted 2017-07-11T07:20:37.273

Reputation: 13 814