# FizzBuzz Reverse Solver

Synopsis: Given the output of a generalised FizzBuzz program, return the list of factors and words used for the program.

## Challenge Description

Imagine a generalised FizzBuzz program that takes in as input a list of factors and words to use and the number to start from. For instance, if the input of this program was

3 2,Ninja 5,Bear 7,Monkey


The program would print out numbers from 3 to 100, replacing numbers divisible by 2 with Ninja, numbers divisible by 5 with Bear, and numbers divisible by 7 with Monkey. For numbers that are divisible then more than one of those terms, the program will concatenate the words, printing things such as NinjaBear or BearMonkey or NinjaMonkey or NinjaBearMonkey. Here's the output of that input:

3
Ninja
Bear
Ninja
Monkey
Ninja
9
NinjaBear
11
Ninja
13
NinjaMonkey
Bear
Ninja
17
Ninja
19
NinjaBear
Monkey
Ninja
23
Ninja
Bear
Ninja
27
NinjaMonkey
29
NinjaBear
31
Ninja
33
Ninja
BearMonkey
Ninja
37
Ninja
39
NinjaBear
41
NinjaMonkey
43
Ninja
Bear
Ninja
47
Ninja
Monkey
NinjaBear
51
Ninja
53
Ninja
Bear
NinjaMonkey
57
Ninja
59
NinjaBear
61
Ninja
Monkey
Ninja
Bear
Ninja
67
Ninja
69
NinjaBearMonkey
71
Ninja
73
Ninja
Bear
Ninja
Monkey
Ninja
79
NinjaBear
81
Ninja
83
NinjaMonkey
Bear
Ninja
87
Ninja
89
NinjaBear
Monkey
Ninja
93
Ninja
Bear
Ninja
97
NinjaMonkey
99
NinjaBear


Note that whenever the program needs to combine words together, it always goes from lowest number to highest number. So it won't print out something like MonkeyBear (since Monkey is a higher number than Bear is).

Your program should take in the output of the generalised FizzBuzz program as input, and output the input given to the generalised FizzBuzz program. In other words, write a "reverse program" for the generalised FizzBuzz program. For instance, given the code block above as input, your program should output 3 2,Ninja 5,Bear, 7,Monkey.

There are some rules that the words will always follow:

• It will always be possible to tell exactly what the factors and words are from the input.
• Each word will begin with a capital letter and will not contain any other capital letters, or numbers.
• Each factor is unique.

## Sample Inputs and Outputs

Input:

Calvins
7
Hobbies
9
10
11
Calvins
13
14
15
Hobbies
17
Calvins
19
20
21
22
23
CalvinsHobbies
25
26
27
28
29
Calvins
31
Hobbies
33
34
35
Calvins
37
38
39
Hobbies
41
Calvins
43
44
45
46
47
CalvinsHobbies
49
50
51
52
53
Calvins
55
Hobbies
57
58
59
Calvins
61
62
63
Hobbies
65
Calvins
67
68
69
70
71
CalvinsHobbies
73
74
75
76
77
Calvins
79
Hobbies
81
82
83
Calvins
85
86
87
Hobbies
89
Calvins
91
92
93
94
95
CalvinsHobbies
97
98
99
100


Output:

6 6,Calvins 8,Hobbies


Input:

FryEggman
7
Am
Fry
The
11
FryAmEggman
13
14
FryThe
Am
17
FryEggman
19
AmThe
Fry
22
23
FryAmEggman
The
26
Fry
Am
29
FryTheEggman
31
Am
Fry
34
The
FryAmEggman
37
38
Fry
AmThe
41
FryEggman
43
Am
FryThe
46
47
FryAmEggman
49
The
Fry
Am
53
FryEggman
The
Am
Fry
58
59
FryAmTheEggman
61
62
Fry
Am
The
FryEggman
67
Am
Fry
The
71
FryAmEggman
73
74
FryThe
Am
77
FryEggman
79
AmThe
Fry
82
83
FryAmEggman
The
86
Fry
Am
89
FryTheEggman
91
Am
Fry
94
The
FryAmEggman
97
98
Fry
AmThe



Output:

6 3,Fry 4,Am 5,The 6,Eggman


Input:

DeliciousTartApplePie
DeliciousCreamPancakeStrawberry
DeliciousProfiterole
DeliciousCream
DeliciousPancake
DeliciousCreamStrawberryTart


Output:

95 1,Delicious 2,Cream 3,Pancake 4,Strawberry 5,Tart 19,Apple 95,Pie 97,Profiterole


You can get the code I used to generate the inputs here.

• Does the list always go up to exactly 100? Commented Jul 25, 2015 at 3:23
• @Dennis Yes, the upper bound is always 100. Commented Jul 25, 2015 at 3:24
• It's just an honor to be in one of your examples. Commented Jul 25, 2015 at 3:30
• This is a much better version of your challenge than was originally in the sandbox :) Commented Jul 25, 2015 at 8:32
• @NinjaBearMonkey I suppose picking names with many words in them made us better examples. Thanks for including me too @Pyrrha! :) Commented Jul 25, 2015 at 14:13

# Pyth, 73 bytes

jd+J-101lK.zjL\,Sm,_-F>2+_Jf}d@KTUKd{smtcdf-@dTGUdf>T\:K


That was certaintly a tough one. I think I've covered all the edge cases, including everything in @MartinBüttner's example, and the no repeat factors example.

At the high level, the program first finds all of the words by chopping up the alphabetical strings on capital letters.

Then, the rows are mapped to whether or not each string appears in the row, and each possible factor is tested to see if it produces the same ordering. If it does, the factor is added to a gobal list, which is check to see if the factor was already present. If it was not already present, the factor is used. The strings are ordered by first appearance in the input, which disambiguates the ordering of strings that each appear only once in the same row.

After that, it's just formatting and printing.

# Scala, 350 characters

(s:String)⇒{def g(a:Int,b:Int):Int=if(b==0)a.abs else g(b,a%b);val(j,q)=(s.lines:\100→Map.empty[String,Int]){case(l,(n,m))⇒if(l(0).isDigit)(n-1,m)else(n-1,m++(Seq(Seq(l(0)))/:l.tail){case(x,c)⇒if(c.isUpper)Seq(c)+:x else (x(0):+c)+:x.tail}.map{t⇒val w=t.mkString;w→g(m.getOrElse(w,n),n)})};s"${j+1}"+q.map{case(k,v)=>s"$v,$k"}.toSeq.sorted.mkString}  not winning... but nice question. ### tested results: scala> (s:String)⇒{def g(a:Int,b:Int):Int=if(b==0)a.abs else g(b,a%b);val(j,q)=(s.lines:\100→Map.empty[String,Int]){case(l,(n,m))⇒if(l(0).isDigit)(n-1,m)else(n-1,m++(Seq(Seq(l(0)))/:l.tail){case(x,c)⇒if(c.isUpper)Seq(c)+:x else (x(0):+c)+:x.tail}.map{t⇒val w=t.mkString;w→g(m.getOrElse(w,n),n)})};s"${j+1}"+q.map{case(k,v)=>s" $v,$k"}.toSeq.sorted.mkString}
res0: String => String = <function1>

scala> res0("""DeliciousTartApplePie
| DeliciousCreamPancakeStrawberry
| DeliciousProfiterole
| DeliciousCream
| DeliciousPancake
| DeliciousCreamStrawberryTart""")
res1: String = 95 1,Delicious 2,Cream 3,Pancake 4,Strawberry 5,Tart 95,Apple 95,Pie 97,Profiterole

scala> res0("""FryEggman
| 7
| Am
| Fry
| The
| 11
| FryAmEggman
| 13
| 14
| FryThe
| Am
| 17
| FryEggman
| 19
| AmThe
| Fry
| 22
| 23
| FryAmEggman
| The
| 26
| Fry
| Am
| 29
| FryTheEggman
| 31
| Am
| Fry
| 34
| The
| FryAmEggman
| 37
| 38
| Fry
| AmThe
| 41
| FryEggman
| 43
| Am
| FryThe
| 46
| 47
| FryAmEggman
| 49
| The
| Fry
| Am
| 53
| FryEggman
| The
| Am
| Fry
| 58
| 59
| FryAmTheEggman
| 61
| 62
| Fry
| Am
| The
| FryEggman
| 67
| Am
| Fry
| The
| 71
| FryAmEggman
| 73
| 74
| FryThe
| Am
| 77
| FryEggman
| 79
| AmThe
| Fry
| 82
| 83
| FryAmEggman
| The
| 86
| Fry
| Am
| 89
| FryTheEggman
| 91
| Am
| Fry
| 94
| The
| FryAmEggman
| 97
| 98
| Fry
| AmThe""")
res2: String = 6 3,Fry 4,Am 5,The 6,Eggman


# Python 2, 366340 331 bytes

This program receive input via stdin.

New approach:

Calculate the factor of only one occurrence's words by the distance from the end of the line. For example (from the last sample): DeliciousTartApplePie Pie is calculate as: [95,19,5,1][0] and Apple is: [95,19,5,1][1].

import sys
import re
d=[(i,re.findall('[A-Z][a-z]*',l)[::-1])for i,l in enumerate(sys.stdin)]
e=101-len(d)
print e," ".join(x+','+y[1:-1]for x,y in sorted({next((j-i for j,t in d if j>i and w in t),[x for x in range(i+e,0,-1)if(i+e)%x==0][d[i][1].index(w)]):w for w,i in{w:i for i,l in d[::-1]for w in l}.items()}.iteritems()))


Old approach:

import sys
import re
l=[(i,re.findall('[A-Z][a-z]*',l))for i,l in enumerate(sys.stdin)]
e=101-len(l)
d={}
for i,s in l:
for w in s[::-1]:
if w not in d.values():
d[next((j-i for j,t in l[i+1:]if w in t),next(m for m in range(i+e,0,-1)if(i+e)%m==0and m not in d))]=w
print e," ".join(x+','+y[1:-1]for x,y in sorted(d.iteritems()))


# Usage:

python FizzBuzzReverseSolver.py < Sample1.txt


# Explanation (of Old approach):

• In general, the program creates a list of line number and list of words (e.g. [(0, []), (1, ['Ninja']), (2, ['Bear']), ...].
• For each word in every line (starting from the end of the line):
• Find the next occurrence of the word and insert the difference and the word to a predefined dictionary.
• If it doesn't found, insert the biggest factor of the line number (including itself) that doesnt already exist in the dictionary and the word to the dictionary.

# 05AB1E, 65 bytes

|ā¤тα©+øUáεDS.uÅ¡J¦}˜ÙDεVXʒнYå}€θ0š¥θ}øí.¡н}εRεćÑRNèš]€{',ý®>šðý


Try it online.

Explanation:

Step 1: Pair all input-lines with their corresponding value:

|            # Get all inputs as a list of strings
ā           # Push a list in the range [1,length] (without popping the list itself)
¤          # Push its last/largest value (without popping)
тα        # Get the absolute difference with 100
©       # Store this value in variable ® (without popping)
+      # Add it to each value in the [1,length]-list
ø            # Create pairs with the list of strings


Try just step 1, one builtin at a time.

Step 2: Get all unique words:

U            # Pop and store the paired-list in variable X
á           # Only leave the letters of each string in the (implicit) input-list
# (note that it'll use the entire list as input after we've used | once)
ε          # Map each string to:
D         #  Duplicate the string
S        #  Convert the copy to a list of characters
.u      #  Check for each character whether it's uppercase
Å¡    #  Split at the truthy indices (while keeping them)
J   #  Join each inner list of characters back to a string
¦  #  Remove the leading item, which is always an empty string
}˜         # After the map: flatten the list of lists
Ù        # And uniquify it


Try just step 2, one builtin at a time.

Step 3: Map each word to a list of pairs containing that word:

D            # Duplicate the list of words
ε           # Map over each:
V          #  Pop and store the current word in variable Y
X         #  Push the paired-list X
ʒ        #  Filter it by:
н       #   Where its first item (the string)
Yå     #   Contains word Y
}        #  Close the filter


Try just step 3.

Step 4: Convert each to the step-size, and pair it with the words:

     €       #  Map over each inner list
θ      #   And only leave the last item (the value)
0š    #  Prepend a 0 to this list
¥   #  Get the deltas/forward-differences
θ  #  And only leave the last one
}ø          # After the map: pair each together with its word


Try just step 4, one builtin at a time.

Step 5: Fix any duplicated items (i.e. Apple & Pie both having value 95 now for the last test case):

í            # Reverse each pair
.¡          # Then group all pairs by:
н         #  Their first item (the value)
}ε         # After the group-by, map over each list of pairs:
R        #  Reverse the list of pairs
ε       #  Map over each pair:
ć      #   Extract head; pop and push remainder-list and first item separated
Ñ     #   Pop this value, and get all its divisors
R    #   Reverse this list
Nè  #   Index the map-index into it
š #   And prepend this value back to the list
]€       # After both maps: flatten the list of lists of pairs one level down


Try just step 5, one builtin at a time.

Step 6: Print the output, including starting item:

{            # Sort this list of pairs (based on their first item, the value)
',ý        '# Join each pair with "," delimiter
®>       # Push value ®, and increase it by 1
š      # Prepend it in front of the list
ðý    # Join the list by spaces
# (after which it is output implicitly as result)


Try just step 6, one builtin at a time.