Le numéro original

36

Des lignes directrices

Scénario

John a un nombre important et il ne veut pas que les autres le voient.

Il a décidé de chiffrer le numéro en procédant comme suit:

Son numéro est toujours une séquence non décroissante (ie. "1123")

Il convertit chaque chiffre en mots anglais. (ie. "123" -> "ONETWOTHREE")

Et puis, réorganisez les lettres au hasard. (ie. "ONETWOTHREE" -> "ENOWTOHEETR")

John sentait que son numéro était en sécurité. En fait, un tel cryptage peut être facilement décrypté :(


Tâche

Étant donné la chaîne cryptée s, votre tâche consiste à la déchiffrer et à renvoyer le numéro d'origine.


Règles

  • C'est le code de golf, donc la réponse la plus courte en octets gagne
  • Vous pouvez supposer que la chaîne d'entrée est toujours valide
  • La chaîne d'entrée ne contient que des lettres majuscules
  • Les numéros d'origine sont toujours classés par ordre croissant
  • Vous pouvez retourner le nombre au format chaîne ou entier
  • Les lettres ne seront mélangées qu'entre un mot, pas entre la chaîne entière.
  • Les chiffres ne seront de 1 à 9 inclus ( ONEà NINE)

Chaîne non brouillée possible

Voici une liste des chaînes juste après leur conversion en chaînes des nombres:

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

Exemples

"NEO" -> 1

"ENOWOT" -> 12

"EONOTWHTERE" -> 123

"SNVEEGHEITNEIN" -> 789

"ENOOWTEERHTRUOFEVIFXISNEVESTHGIEENIN" -> 123456789

"NOEWOTTOWHEERT" -> 1223

Amorris
la source
5
Dans tous les cas de test, seules les lettres d'un mot sont mélangées, pas les lettres entre les mots. Cela sera-t-il toujours le cas?
xnor
1
@xnor Ce sera toujours le cas. J'ai édité la question.
Amorris
1
alors vous devez changer cela ".... (c.-à-d." ONETWOTHREE "->" TTONWOHREEE ")"
J42161217 Le
2
@ TessellatingHeckler: Une séquence non strictement croissante correspond au moment où le nombre suivant peut être identique à celui de l'ex précédent. 1-1-1-2-2-3 (non strictement croissante) par opposition à 1-2-3-4-5 (strictement croissante)
koita_pisw_sou
1
Techniquement parlant, il s'agit d'un encodage, pas d'un cryptage, car il n'y a pas de clé.
Patrick Roberts

Réponses:

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?

Tout d’abord, notez que les mots eux-mêmes (et donc leurs anagrammes) peuvent tous être remplacés par des mots de longueur 4 en supprimant tous les Rs, G et S et en remplaçant les Os par deux caractères (par exemple, "12") et les X par trois ( dites "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

Nous pouvons ensuite mapper le produit des ordinaux de ces caractères sur les nombres de 1 à 9 en utilisant une arithmétique modulo, selon notre choix (le "12345"), puis les rechercher dans une liste de chiffres réordonnée. Le code convertit en fait d’abord les caractères puis remplace les ordinaux, mais il est également possible de créer 37 octets avec des caractères, par exemple "DIAAE" ( essayez-le ).

ḟ“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
la source
Your answer is literally the only answer on this page that returns a correct value for: NINEONENIENOENNNIENOENNEINEONEINEONNENIENOINNEINENINNEINENIENNIENNNNIENNEININENIENNENINEINENINENNIEINNEINNENNIENIN.
Magic Octopus Urn
+Infinity points.
Magic Octopus Urn
Thanks! (that threw me because there are zero width spaces to the code block in the comment, but (whew) it does work)
Jonathan Allan
It's not a valid input anyway ;).
Magic Octopus Urn
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
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
la source
What's Py3K?...
CalculatorFeline
Apologies, edited (it's the former name of python 3)
Score_Under
1
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!]
Coty Johnathan Saxman
Also, you might be able to save 6 bytes using literal \x83, \x8e, and \x92 in the string.
CalculatorFeline
@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
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
la source
What an interesting use of vars!
xnor
@xnor it was ovs how learned me that for other golfs :)))
mdahmoune
Very clever. Have a +1 for adapting my answer (as flawed as it was originally).
Draco18s
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
la source
fails for ENOOWTWTOWOT
Titus
@Titus is now fixed. I have misunderstand the question
Jörg Hülsermann
Yea the examples are somewhat misleading. Wow that did cost! Would you break that down?!
Titus
@Titus I think I have reach the limit to find another way as your approach
Jörg Hülsermann
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
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
la source
1
Great, but there is a problem with counting 9... I think it may be i-x-g-f+u
mdahmoune
@mdahmoune Shoot, you're right. I messed that one up. :<
Draco18s
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
@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
@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
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
la source
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
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
la source
2
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
TheLethalCoder
For more tips see Tips for code-golfing in C#.
TheLethalCoder
@TheLethalCoder Great tips, thanks!
SamWhan
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
On a side note it is usually easier to use TIO for your TIO's!
TheLethalCoder
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
la source
wait what?? "axbxc".split`x`.join``. How is this called? Can't seem to find anything on google.
Qwerty
@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
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
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
1
f(s.slice(y)) is always a string so you don't need the ''+ before it.
Neil
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
la source
There is an issue with your code. Try passing the string "EIGHTNINE" into it :)
Amorris
@Amorris fixed for 0 bytes.
PurkkaKoodari
I think it doesn't work for "VENESGTHIEENNI"
J42161217
I second @Jenny_mathy
Amorris
@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
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
la source
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
la source
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
la source
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
Thanks @PunPun1000. I figured there must be a way to do that but gave up after not finding it quickly.
Kytheron
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
la source
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
la source
You need to include the definition of N and using namespace std; into your byte count.
orlp
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
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
1
Can you define a macro to save a few bytes? repl.it/JY7k
Zacharý
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
la source
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
la source
1
Erf... wrong result for "ENOOWTEERHTRUOFEVIFXISNEVESTHGIEENIN" :(
Olivier Grégoire
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
@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
@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
1
Well, looks like I found an even shorter way while redoing this challenge :p
Olivier Grégoire
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
la source
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
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
la source
I was just suggesting you had the green mark rather than me and I noticed a bug: FURONESEV returns FUR1SEV :(
Jonathan Allan
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
la source
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
Also, I hope you don't mind me posting any advice, if you'd prefer I'll refrain. :)
Dom Hastings
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
la source
You have to include def f(s): in your byte count, this is not an anonymouos function
Mr. Xcoder
Also you can replace while len(s)>0 with while len(s)
Mr. Xcoder
@Mr.Xcoder thanks for that clarification
Chase Vogeli
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
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
la source