我有一个可能的密码列表,我需要在此列表中附加每个密码的简单转换。说我的清单是
['sauce', 'banana']
我在这里显示了一系列转换。
'A' - > '@'
'S' - > '$'
然后,我希望将每个可能的转换添加到列表中。所以现在列表看起来应该像
['$auce', 's@uce', '$@uce', 'b@nana', 'ban@na',
'banan@', 'b@n@na', 'b@nan@,' 'ban@n@', 'b@n@n@']
我将如何在Python中执行此操作?
我首先尝试创建一个完成所有转换的函数。然后我拿了那个变换后的字符串,基本上用原始字符串做了一个交叉产品。然而,这会导致很多重复,而且看起来有点笨拙。
功能:
def symbolize(s):
options = {
'a': '@',
'S': '$'
}
copy = ''
for i in range(len(s)):
if s[i] in options:
copy += options[s[i]]
else:
copy += s[i]
return copy
然后是交叉产品:
for x in range(len(candidates)):
candidates += list(''.join(t) for t in itertools.product(
*zip(candidates[x], symbolize(candidates[x]))))
答案 0 :(得分:3)
from itertools import product
def all_versions_of_word(word, alt_chars, skip_orig=True):
chars = [ch + alt_chars.get(ch, "") for ch in word]
combos = product(*chars)
if skip_orig and word: next(combos) # drop the first item
return ("".join(c) for c in combos)
def transform_passwords(passwords, alt_chars={"a":"@", "s":"$"}):
for word in passwords:
yield from all_versions_of_word(word, alt_chars)
像
一样运行>>> list(transform_passwords(['sauce', 'banana']))
['s@uce',
'$auce',
'$@uce',
'banan@',
'ban@na',
'ban@n@',
'b@nana',
'b@nan@',
'b@n@na',
'b@n@n@']
答案 1 :(得分:2)
如果你想要你可以使用递归,虽然pytohn将它限制在2000的深度:
创建映射和列表:
lst = ['sauce', 'banana']
mapping = {'a':'@', 's':'$'}
现在递归地生成所有可能性(包括根本没有替换):
def opts(_mapping, _str):
if not _str:
yield ""
else:
for opt in opts(_mapping, _str[1:]):
if _str[0] in _mapping:
yield _mapping[_str[0]] + opt
yield _str[0] + opt
输出:
[list(opts(mapping, st)) for st in lst]
=> [['$@uce', 's@uce', '$auce', 'sauce'], ['b@n@n@', 'ban@n@', 'b@nan@', 'banan@', 'b@n@na', 'ban@na', 'b@nana', 'banana']]
答案 2 :(得分:0)
我真的很开心地挖掘这个答案!这里有很多迭代字符串,但我喜欢我的答案!
import functools
def transform(pwd, subs):
result = {pwd}
stack = [pwd]
# contains all resolved strings left to permute on
while True:
pwd = stack.pop()
# grab a password
if not stack and not any(i in subs for i in pwd):
return result
# if the stack is empty and is no way to permute further,
# then return our result.
for idx,ch in enumerate(pwd):
if ch in subs:
repl = subs[ch]
transformation = pwd[:idx]+repl+pwd[idx+1:]
# transformation is our transformed word
result.add(transformation)
# add our transformation to our result set
stack.append(transformation)
# and toss it on our stack to transform further
def transform_multiple(pwds, subs):
return functools.reduce(set.union,
(transform(pwd, subs) for pwd in pwds))
样本:
In [55]: transform_multiple(['banana', 'sauce','ananas'], {'a':'@', 's':'$'})
Out[55]:
{'$@uce',
'$auce',
'@n@n@$',
'@n@n@s',
'@n@na$',
'@n@nas',
'@nan@$',
'@nan@s',
'@nana$',
'@nanas',
'an@n@$',
'an@n@s',
'an@na$',
'an@nas',
'anan@$',
'anan@s',
'anana$',
'ananas',
'b@n@n@',
'b@n@na',
'b@nan@',
'b@nana',
'ban@n@',
'ban@na',
'banan@',
'banana',
's@uce',
'sauce'}
如果我要花更多时间在这上面,我可能会删除if not stack and not any(...)
调用并在for idx,ch in enumerate
循环中放置一个标记,标记是否有变化对该字符串进行处理,然后在循环结束后测试if not flag and not stack: return result
。每次循环时,这将为我们节省pwd
和len(pwd)
成员资格测试的整个迭代次数。