Вы можете построить всю перестановку с помощью обратного отслеживания.
Сначала дикт будет более полезным, если его поменять местами, так что сделайте это:
from collections import defaultdict
orig_str = "aabbaaa"
d = {'A': 'a', 'B': 'a', 'C': 'b', 'D': 'a', 'E': 'b', 'F': 'a', 'G': 'b'}
reverse_d = defaultdict(list)
for k, el in d.items():
reverse_d[el].append(k)
И здесь мы имеем reverse_d = {'a': ['A', 'B', 'D', 'F'], 'b': ['C', 'E', 'G']}
Далее мы можем написать нашу функцию обратного отслеживания, которая для любого символа строки упорядочит возможности:
def permut(orig_str, index, chars_till_now):
if index == len(orig_str):
print("".join(chars_till_now))
return
chars = chars_till_now[:]
chars.append("")
for possibility in reverse_d[orig_str[index]]:
chars[-1] = possibility
permut(orig_str, index+1, chars)
Вы можете изменить функцию, чтобы сохранить перестановку, а не печатать или передавать определите c словарь, а не используйте один глобальный; это зависит от того, что вам нужно.
Для вызова функции просто:
permut(orig_str, 0, [])