The code below takes a string, then in p = there is a mapping for every index that can be changed and with what characters. For example d1 is atp[0], so the character a (at string[0]) can be replaced by d or 1. The number of characters that have to change at a time is limited to the number 3.
from itertools import combinations, product
string = "abc123"
p = ["d1", "c3", "", "", "0", "56"]
d = {idx: (v if string[idx] in v else string[idx]+v) for idx, v in enumerate(p)}
all_of_em = (''.join(whatever) for whatever in product(*d.values()))
fewer = [w for w in all_of_em if sum(a != b for a, b in zip(w, string)) == 3]
with open("list.txt","w") as f:
for w in fewer:
f.write(w+"\n")
As a result of the above code, we find all possible combinations if we change 3 places in a string with the specified alternative characters in p.
acc105
acc106
a3c105
a3c106
dbc105
dbc106
dcc125
dcc126
dcc103
d3c125
d3c126
d3c103
1bc105
1bc106
1cc125
1cc126
1cc103
13c125
13c126
13c103
The goal is to print the results faster, for example these lines should be changed I think:
with open("list.txt","w") as f:
for w in fewer:
f.write(w+"\n")
So the output will be saved as python3 py.py >> list.txt
Will enjoy to learn from your solution.
Your solution is based on a brute force approach. You are generating all possible alternative strings and then filtering out the ones that do not meet the criteria of only 3 changes. A better approach would be to look only at those combinations that will meet the criteria. I will ignore the part of saving to a file, since it will be the same for both solutions. A faster solution would be:
def change_string(input_string, mapping, replace=3):
input_string = list(input_string)
to_replace = dict()
for idx, replacement in enumerate(mapping):
if not replacement: continue
to_replace[idx] = replacement
if input_string[idx] in replacement:
to_replace[idx] = [char for char in replacement if char != mapping[idx]]
for indices in combinations(to_replace, r=replace):
for chars in product(*[to_replace[index] for index in indices]):
temp = input_string[:]
for index, char in zip(indices, chars):
temp[index] = char
yield ''.join(temp)
Explanation
I change the input string to a list, so I can do the replacement faster, since lists are mutable and strings are not.
Then I filter the mapping (p) to represent only indices that are going to be changed. This removes all empty strings and provides me with the indices that I have to look at.
to_replace = dict()
for idx, replacement in enumerate(mapping):
if not replacement: continue
to_replace[idx] = replacement
if input_string[idx] in replacement:
to_replace[idx] = [char for char in replacement if char != mapping[idx]]
Note: I also make sure that the values in mapping are unequal to the original string values, which might not be what you want.
Then I create all possible combinations of indices with the required length (replace=3).
for indices in combinations(to_replace, r=replace):
Using your example this will contain the following group of indices:
(0, 1, 4)
(0, 1, 5)
(0, 4, 5)
(1, 4, 5)
Then I create all possible character combinations from those indices:
for chars in product(*[to_replace[index] for index in indices]):
For example with indices (0, 1, 4) or the values ('d1', 'c3', '0'):
('d', 'c', '0')
('d', '3', '0')
('1', 'c', '0')
('1', '3', '0')
Are all the character combinations produced.
Then I create a copy of the input string (note it is a list, so we can perform fast replacements) and replace the characters at the correct indices.
Comparison
Your function
def OP(input_string, replace=3):
p = ["d1", "c3", "", "", "0", "56"]
d = {idx: (v if input_string[idx] in v else input_string[idx] + v) for idx, v in enumerate(p)}
all_of_em = (''.join(whatever) for whatever in product(*d.values()))
fewer = [w for w in all_of_em if sum(a != b for a, b in zip(w, input_string)) == replace]
return fewer
Replace is 3
print(timeit.timeit("OP('abc123')", setup="from __main__ import OP", number=100_000))
# 5.6281933 seconds
print(timeit.timeit("list(change_string('abc123', ['d1', 'c3', '', '', '0', '56']))",
setup="from __main__ import change_string", number=100_000))
# 1.3682368 seconds
Which is about 3 times as fast, now the interesting part is to see what happens if we increase the replace value to 4
Replace is 4
print(timeit.timeit("OP('abc123', replace=4)", setup="from __main__ import OP", number=100_000))
# 5.5450302 seconds
print(timeit.timeit("list(change_string('abc123', ['d1', 'c3', '', '', '0', '56'], replace=4))",
setup="from __main__ import change_string", number=100_000))
# 0.6179974 seconds
A whooping 9 times faster, since my solution only has to check a few combinations.
Similar increase can be seen with using replace is 2 or 1.
Using a generator function will avoid creation and manipulation of large lists in memory. You can write it to the file as a single block of text using join.
def replace(S,R,N):
if not N: yield S; return
for i,chars in enumerate(R[:(1-N) or None]):
for c in chars:
yield from (S[:i]+c+s for s in replace(S[i+1:],R[i+1:],N-1))
def writeReplace(S,R,N):
with open("list.txt","w") as f:
f.write("\n".join(replace(S,R,3)))
S = "abc123"
R = ["d1", "c3", "", "", "0", "56"]
writeReplace(S,R,3)
dcc103
dcc125
dcc126
d3c103
d3c125
d3c126
dbc105
dbc106
1cc103
1cc125
1cc126
13c103
13c125
13c126
1bc105
1bc106
acc105
acc106
a3c105
a3c106
This is roughly 2.5x faster.
Related
Given a word and a dictionary of replacement characters, I need to form a Combination of characters based on the replacement
Input
word = 'accompanying'
substitutions={'c':['$'], 'a': ['4'], 'g': ['9']}
Output
{'a$$ompanyin9', 'ac$ompanyin9','a$companyin9','4ccomp4nying', '4$$omp4nying',
'4$comp4nying','4c$omp4nying', '4ccomp4nyin9', 'a$$ompanying', 'a$companying', 'ac$ompanying',
'accompanyin9', 'accompanying', '4$$omp4nyin9', '4$comp4nyin9', '4c$omp4nyin9','etc.,'}
I wrote a code, But it does not provide me all the combinations which I am expecting
Sample Code
from itertools import product
substitutions={'c':['$'], 'a': ['4'], 'g': ['9']}
for key in substitutions.keys():
if key not in substitutions[key]:
substitutions[key].append(key)
wordPossibilities = []
word = 'accompanying'
for substitute in [zip(substitutions.keys(),ch) for ch in product(*substitutions.values())]:
temp=word
for replacement in substitute:
temp=temp.replace(*replacement)
wordPossibilities.append(temp)
print(set(wordPossibilities))
My Output
{'4$$omp4nyin9', 'a$$ompanyin9', 'a$$ompanying', 'accompanyin9',
'accompanying', '4ccomp4nyin9', '4$$omp4nying', '4ccomp4nying'}
My code replaces all characters in the provided string if found a replacement. How do I make replacements based on Indexes to find all possible combinations?
It is clean and straightforward to use a generator with recursion:
word = 'accompanying'
subs={'c':['$'], 'a': ['4'], 'g': ['9']}
def get_subs(d, c = []):
if not d:
yield ''.join(c)
else:
for i in [d[0], *subs.get(d[0], [])]:
yield from get_subs(d[1:], c+[i])
print(list(get_subs(word)))
Output:
['accompanying', 'accompanyin9', 'accomp4nying', 'accomp4nyin9', 'ac$ompanying', 'ac$ompanyin9', 'ac$omp4nying', 'ac$omp4nyin9', 'a$companying', 'a$companyin9', 'a$comp4nying', 'a$comp4nyin9', 'a$$ompanying', 'a$$ompanyin9', 'a$$omp4nying', 'a$$omp4nyin9', '4ccompanying', '4ccompanyin9', '4ccomp4nying', '4ccomp4nyin9', '4c$ompanying', '4c$ompanyin9', '4c$omp4nying', '4c$omp4nyin9', '4$companying', '4$companyin9', '4$comp4nying', '4$comp4nyin9', '4$$ompanying', '4$$ompanyin9', '4$$omp4nying', '4$$omp4nyin9']
However, itertools.product can be used for a shorter solution:
from itertools import product as prod
s = ''.join('{}' if i in subs else i for i in word)
result = [s.format(*i) for i in prod(*[[i, *subs[i]] for i in word if i in subs])]
Output:
['accompanying', 'accompanyin9', 'accomp4nying', 'accomp4nyin9', 'ac$ompanying', 'ac$ompanyin9', 'ac$omp4nying', 'ac$omp4nyin9', 'a$companying', 'a$companyin9', 'a$comp4nying', 'a$comp4nyin9', 'a$$ompanying', 'a$$ompanyin9', 'a$$omp4nying', 'a$$omp4nyin9', '4ccompanying', '4ccompanyin9', '4ccomp4nying', '4ccomp4nyin9', '4c$ompanying', '4c$ompanyin9', '4c$omp4nying', '4c$omp4nyin9', '4$companying', '4$companyin9', '4$comp4nying', '4$comp4nyin9', '4$$ompanying', '4$$ompanyin9', '4$$omp4nying', '4$$omp4nyin9']
Obviously, you need to rewrite your logic to consider individual instances of the desired letters, rather than each unique letter. Find all occurrences of desired letters; use itertools to get the power set; make the indicated substitutions for each element of the power set. power_set comes from this SO answer. I've left the code "exploded" in some places to show the logic more readily. You will likely want to wrap the final loop into a one-line return expression.
from itertools import chain, combinations
def power_set(iterable):
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
substitutions={'c':['$'], 'a': ['4', 'a'], 'g': ['9']}
word = 'accordingly'
# Get index of each desired letter and its poosible substitutions
sub_idx = [(pos, letter, sub_letter) for pos, letter in enumerate(word)
if letter in list(substitutions.keys()) for sub_letter in substitutions[letter]]
print("Replacement set", sub_idx)
for possibility in power_set(sub_idx):
# Make each of the substitutions indicated in the power set
new_word = list(word)
for pos, _, sub_letter in possibility:
new_word[pos] = sub_letter
print(''.join(new_word))
Output:
Replacement set [(0, 'a', '4'), (0, 'a', 'a'), (1, 'c', '$'), (2, 'c', '$'), (8, 'g', '9')]
accordingly
4ccordingly
accordingly
a$cordingly
ac$ordingly
accordin9ly
accordingly
4$cordingly
4c$ordingly
4ccordin9ly
a$cordingly
ac$ordingly
accordin9ly
a$$ordingly
a$cordin9ly
ac$ordin9ly
a$cordingly
ac$ordingly
accordin9ly
4$$ordingly
4$cordin9ly
4c$ordin9ly
a$$ordingly
a$cordin9ly
ac$ordin9ly
a$$ordin9ly
a$$ordingly
a$cordin9ly
ac$ordin9ly
4$$ordin9ly
a$$ordin9ly
a$$ordin9ly
I am trying to do slicing in string "abcdeeefghij", here I want the slicing in such a way that whatever input I use, i divide the output in the format of a list (such that in one list element no alphabets repeat).
In this case [abcde,e,efghij].
Another example is if input is "aaabcdefghiii". Here the expected output is [a,a,acbdefghi,i,i].
Also amongst the list if I want to find the highest len character i tried the below logic:
max_str = max(len(sub_strings[0]),len(sub_strings[1]),len(sub_strings[2]))
print(max_str) #output - 6
which will yield 6 as the output, but i presume this logic is not a generic one: Can someone suggest a generic logic to print the length of the maximum string.
Here is how:
s = "abcdeeefghij"
l = ['']
for c in s: # For character in s
if c in l[-1]: # If the character is already in the last string in l
l.append('') # Add a new string to l
l[-1] += c # Add the character to either the last string, either new, or old
print(l)
Output:
['abcde', 'e', 'efghij']
Use a regular expression:
import re
rx = re.compile(r'(\w)\1+')
strings = ['abcdeeefghij', 'aaabcdefghiii']
lst = [[part for part in rx.split(item) if part] for item in strings]
print(lst)
Which yields
[['abcd', 'e', 'fghij'], ['a', 'bcdefgh', 'i']]
You would loop over the characters in the input and start a new string if there is an existing match, otherwise join them onto the last string in the output list.
input_ = "aaabcdefghiii"
output = []
for char in input_:
if not output or char in output[-1]:
output.append("")
output[-1] += char
print(output)
To avoid repetition of alphabet within a list element repeat, you can greedily track what are the words that are already in the current list. Append the word to your answer once you detected a repeating alphabet.
from collections import defaultdict
s = input()
ans = []
d = defaultdict(int)
cur = ""
for i in s:
if d[i]:
ans.append(cur)
cur = i # start again since there is repeatition
d = defaultdict(int)
d[i] = 1
else:
cur += i #append to cur since no repetition yet
d[i] = 1
if cur: # handlign the last part
ans.append(cur)
print(ans)
An input of aaabcdefghiii produces ['a', 'a', 'abcdefghi', 'i', 'i'] as expected.
I have string below,and I want to get list,dict,var from this string.
How can I to split this string to specific format?
s = 'list_c=[1,2],a=3,b=1.3,c=abch,list_a=[1,2],dict_a={a:2,b:3}'
import re
m1 = re.findall (r'(?=.*,)(.*?=\[.+?\],?)',s)
for i in m1 :
print('m1:',i)
I only get result 1 correctly.
Does anyone know how to do?
m1: list_c=[1,2],
m1: a=3,b=1.3,c=abch,list_a=[1,2],
Use '=' to split instead, then you can work around with variable name and it's value.
You still need to handle the type casting for values (regex, split, try with casting may help).
Also, same as others' comment, using dict may be easier to handle
s = 'list_c=[1,2],a=3,b=1.3,c=abch,list_a=[1,2],dict_a={a:2,b:3}'
al = s.split('=')
var_l = [al[0]]
value_l = []
for a in al[1:-1]:
var_l.append(a.split(',')[-1])
value_l.append(','.join(a.split(',')[:-1]))
value_l.append(al[-1])
output = dict(zip(var_l, value_l))
print(output)
You may have better luck if you more or less explicitly describe the right-hand side expressions: numbers, lists, dictionaries, and identifiers:
re.findall(r"([^=]+)=" # LHS and assignment operator
+r"([+-]?\d+(?:\.\d+)?|" # Numbers
+r"[+-]?\d+\.|" # More numbers
+r"\[[^]]+\]|" # Lists
+r"{[^}]+}|" # Dictionaries
+r"[a-zA-Z_][a-zA-Z_\d]*)", # Idents
s)
# [('list_c', '[1,2]'), ('a', '3'), ('b', '1.3'), ('c', 'abch'),
# ('list_a', '[1,2]'), ('dict_a', '{a:2,b:3}')]
The answer is like below
import re
from pprint import pprint
s = 'list_c=[1,2],a=3,b=1.3,c=abch,list_a=[1],Save,Record,dict_a={a:2,b:3}'
m1 = re.findall(r"([^=]+)=" # LHS and assignment operator
+r"([+-]?\d+(?:\.\d+)?|" # Numbers
+r"[+-]?\d+\.|" # More numbers
+r"\[[^]]+\]|" # Lists
+r"{[^}]+}|" # Dictionaries
+r"[a-zA-Z_][a-zA-Z_\d]*)", # Idents
s)
temp_d = {}
for i,j in m1:
temp = i.strip(',').split(',')
if len(temp)>1:
for k in temp[:-1]:
temp_d[k]=''
temp_d[temp[-1]] = j
else:
temp_d[temp[0]] = j
pprint(temp_d)
Output is like
{'Record': '',
'Save': '',
'a': '3',
'b': '1.3',
'c': 'abch',
'dict_a': '{a:2,b:3}',
'list_a': '[1]',
'list_c': '[1,2]'}
Instead of picking out the types, you can start by capturing the identifiers. Here's a regex that captures all the identifiers in the string (for lowercase only, but see note):
regex = re.compile(r'([a-z]|_)+=')
#note if you want all valid variable names: r'([a-z]|[A-Z]|[0-9]|_)+'
cases = [x.group() for x in re.finditer(regex, s)]
This gives a list of all the identifiers in the string:
['list_c=', 'a=', 'b=', 'c=', 'list_a=', 'dict_a=']
We can now define a function to sequentially chop up s using the
above list to partition the string sequentially:
def chop(mystr, mylist):
temp = mystr.partition(mylist[0])[2]
cut = temp.find(mylist[1]) #strip leading bits
return mystr.partition(mylist[0])[2][cut:], mylist[1:]
mystr = s[:]
temp = [mystr]
mylist = cases[:]
while len() > 1:
mystr, mylist = chop(mystr, mylist)
temp.append(mystr)
This (convoluted) slicing operation gives this list of strings:
['list_c=[1,2],a=3,b=1.3,c=abch,list_a=[1,2],dict_a={a:2,b:3}',
'a=3,b=1.3,c=abch,list_a=[1,2],dict_a={a:2,b:3}',
'b=1.3,c=abch,list_a=[1,2],dict_a={a:2,b:3}',
'c=abch,list_a=[1,2],dict_a={a:2,b:3}',
'list_a=[1,2],dict_a={a:2,b:3}',
'dict_a={a:2,b:3}']
Now cut off the ends using each successive entry:
result = []
for x in range(len(temp) - 1):
cut = temp[x].find(temp[x+1]) - 1 #-1 to remove commas
result.append(temp[x][:cut])
result.append(temp.pop()) #get the last item
Now we have the full list:
['list_c=[1,2]', 'a=3', 'b=1.3', 'c=abch', 'list_a=[1,2]', 'dict_a={a:2,b:3}']
Each element is easily parsable into key:value pairs (and is also executable via exec).
I have a string where a character ('#') needs to be replaced by characters from a list of one or more characters "in order" and "periodically".
So for example I have
'ab#cde##fghi#jk#lmno###p#qrs#tuvwxy#z'
and want
'ab1cde23fghi1jk2lmno312p3qrs1tuvwxy2z'
for replace_chars = ['1', '2', '3']
The problem is that in this example there are more # in the string
than I have replacers.
This is my try:
result = ''
replace_chars = ['1', '2', '3']
string = 'ab#cde##fghi#jk#lmno###p#qrs#tuvwxy#z'
i = 0
for char in string:
if char == '#':
result += replace_chars[i]
i += 1
else:
result += char
print(result)
but this only works of course if there are not more than three # in the original string and otherwise I get IndexError.
Edit: Thanks for the answers!
Your code could be fixed by adding the line i = i%len(replace_chars) as the last line of your if clause. This way you will be taking the remainder from the division of i by the length of your list of replacement characters.
The shorter solution is to use a generator that periodically spits out replacement characters.
>>> from itertools import cycle
>>> s = 'ab#cde##fghi#jk#lmno###p#qrs#tuvwxy#z'
>>> replace_chars = ['1', '2', '3']
>>>
>>> replacer = cycle(replace_chars)
>>> ''.join([next(replacer) if c == '#' else c for c in s])
'ab1cde23fghi1jk2lmno312p3qrs1tuvwxy2z'
For each character c in your string s, we get the next replacement character from the replacer generator if the character is an '#', otherwise it just gives you the original character.
For an explanation why I used a list comprehension instead of a generator expression, read this.
Generators are fun.
def gen():
replace_chars = ['1', '2', '3']
while True:
for rc in replace_chars:
yield rc
with gen() as g:
s = 'ab#cde##fghi#jk#lmno###p#qrs#tuvwxy#z'
s = ''.join(next(g) if c == '#' else c for c in s)
As PM 2Ring suggested, this is functionally the same as itertools.cycle. The difference is that itertools.cycle will hold an extra copy of the list in memory which may not be necessary.
itertools.cycle source:
def cycle(iterable):
saved = []
for element in iterable:
yield element
saved.append(element)
while saved:
for element in saved:
yield element
You could also keep your index logic once you use modulo, using a list comp by using itertools.count to keep track of where you are:
from itertools import count
cn, ln = count(), len(replace_chars)
print("".join([replace_chars[next(cn) % ln] if c == "#" else c for c in string]))
ab1cde23fghi1jk2lmno312p3qrs1tuvwxy2z
I think it is better to not iterate character-by-character, especially for long string with lengthy parts without #.
from itertools import cycle, chain
s = 'ab#cde##fghi#jk#lmno###p#qrs#tuvwxy#z'
replace_chars = ['1', '2', '3']
result = ''.join(chain.from_iterable(zip(s.split('#'), cycle(replace_chars))))[:-1]
I don't know how to efficiently kill last char [:-1].
I have just come across an interesting interview style type of question which I couldn't get my head around.
Basically, given a number to alphabet mapping such that [1:A, 2:B, 3:C ...], print out all possible combinations.
For instance "123" will generate [ABC, LC, AW] since it can be separated into 12,3 and 1,23.
I'm thinking it has to be some type of recursive function where it checks with windows of size 1 and 2 and appending to a previous result if it's a valid letter mapping.
If anyone can formulate some pseudo/python code that'd be much appreciated.
So I managed to hack together an answer, it's not as pythonic as I'd like and there may be some redundancies, but it works with the 123 example to output ABC,AW, and LC.
I'll probably clean it up tomorrow (or if someone wants to clean it up), just posting it in case someone is also working on it and is wondering.
def num_to_alphabet(numbers, ans = ""):
if not numbers:
print ans
numbers = str(numbers)
window = numbers[:2]
alph = string.uppercase
ans = ans[:]
ans2 = ans[:]
window_val = ""
try:
if window[0]:
val = int(numbers[0])-1
if alph[val]:
ans += alph[val]
num_to_alphabet(numbers[1:], ans)
if window[1]:
val = int(window) -1
if alph[val]:
ans2 += alph[val]
if len(window) > 1:
num_to_alphabet(numbers[2:],ans2)
else:
num_to_alphabet(numbers[1:],ans2)
except IndexError:
pass
As simple as a tree
Let suppose you have give "1261"
Construct a tree with it a Root .
By defining the node(left , right ) , where left is always direct map and right is combo
version suppose for the if you take given Number as 1261
1261 ->
(1(261) ,12(61)) -> 1 is left-node(direct map -> a) 12 is right node(combo-map1,2->L)
(A(261) , L(61)) ->
(A(2(61),26(1))) ,L(6(1)) ->
(A(B(6(1)),Z(1)) ,L(F(1))) ->
(A(B(F(1)),Z(A)) ,L(F(A))) ->
(A(B(F(A)),Z(A)) ,L(F(A)))
so now you have got all the leaf node..
just print all paths from root to leaf node , this gives you all possible combinations .
like in this case
ABFA , AZA , LFA
So once you are done with the construction of tree just print all paths from root to node
which is your requirement .
charMap = {'1':'A', '2':'B' ... }
def getNodes(str):
results = []
if len(str) == 0: return results
c = str[0]
results.append(c)
results = results.join(c.join(getNodes(str[1:])))
if str[:2] in charMap.keys(): results = results.join(c.join(getNodes(str[2:])))
return results
def mapout(nodes):
cArray = []
for x in nodes:
cx = ''
for y in x:
cx = cx + charMap.get(y)
cArray.append(cx)
return cArray
res = getNodes('12345')
print(mapout(res))
Untested, but I believe this is along the lines of what you're looking for.
The following answer recursively tries all possibilities at the current position (there are more than two!) and goes on with the remainder of the string. That's it.
from string import ascii_uppercase
def alpha_combinations(s):
if len(s) == 0:
yield ""
return
for size in range(1, len(s) + 1):
v = int(s[:size])
if v > 26:
break
if v > 0:
c = ascii_uppercase[v - 1]
for ac in alpha_combinations(s[size:]):
yield c + ac
print(list(alpha_combinations(input())))
It expects a number as a string. It gives correct output for 101010 (['AAJ', 'AJJ', 'JAJ', 'JJJ']). (I think some of the other solutions don't handle zeroes correctly.)
So, I wanted to tackle this as well, since it’s actually a cool problem. So here goes my solution:
If we ignore the translations to strings for now, we are essentially looking for partitions of a set. So for the input 123 we have a set {1, 2, 3} and are looking for partitions. But of those partitions, only those are interesting which maintain the original order of the input. So we are actually not talking about a set in the end (where order doesn’t matter).
Anyway, I called this “ordered partition”—I don’t know if there actually exists a term for it. And we can generate those ordered partitions easily using recursion:
def orderedPartitions(s):
if len(s) == 0:
yield []
return
for i in range(1, len(s)+1):
for p in orderedPartitions(s[i:]):
yield [s[:i]] + p
For a string input '123', this gives us the following partions, which is exactly what we are looking for:
['1', '2', '3']
['1', '23']
['12', '3']
['123']
Now, to get back to the original problem which is asking for translations to strings, all we need to do is check each of those partitions, if they contain only valid numbers, i.e. 1 to 26. And if that is the case, translate those numbers and return the resulting string.
import string
def alphaCombinations(s):
for partition in orderedPartitions(str(s)):
# get the numbers
p = list(map(int, partition))
# skip invalid numbers
if list(filter(lambda x: x < 1 or x > 26, p)):
continue
# yield translated string
yield ''.join(map(lambda i: string.ascii_uppercase[i - 1], p))
And it works:
>>> list(alphaCombinations(123))
['ABC', 'AW', 'LC']
>>> list(alphaCombinations(1234))
['ABCD', 'AWD', 'LCD']
>>> list(alphaCombinations(4567))
['DEFG']
I still am not sure of the description, but this Python script first partitions the num into its 'breaks' then tries each break member as a whole as an index into its corresponding character; then converts each digit of the member into letters of a word. Both contributions are shown before showing the sum total of all conversions to letters/words for the num "123"
>>> import string
>>> mapping ={str(n):ch for n,ch in zip(range(1,27), string.ascii_uppercase)}
>>> num = '123'
>>> [[num[:i], num[i:]] for i in range(len(num)+1)]
[['', '123'], ['1', '23'], ['12', '3'], ['123', '']]
>>> breaks = set(part for part in sum(([num[:i], num[i:]] for i in range(len(num)+1)), []) if part)
>>> breaks
{'123', '12', '3', '1', '23'}
>>> as_a_whole = [mapping[p] for p in breaks if p in mapping]
>>> as_a_whole
['L', 'C', 'A', 'W']
>>> by_char = [''.join(mapping[n] for n in p) for p in breaks]
>>> by_char
['ABC', 'AB', 'C', 'A', 'BC']
>>> everything = sorted(set(as_a_whole + by_char))
>>> everything
['A', 'AB', 'ABC', 'BC', 'C', 'L', 'W']
>>>