# -*- coding: utf-8 -*-
'''
Python程序员面试算法宝典---解题总结: 第5章 字符串 5.1 如何求一个字符串的所有排列
题目:
设计一个程序,当输入一个字符串时,要求输出这个字符串的所有排列。
例如输入字符串abc,要求输出由字符a、b、c所能排列出来的所有字符串:
abc,acb,bac,bca,cba,cab。
分析:
可以采用回溯来实现字符串的排列。
具体实现方式是: 将字符不断加入到结果字符串中,
一旦结果字符串的长度为给定字符串的长度,则输出结果。
需要考虑重复问题。
实际每次选取下一个字符的时候都需要从整个字符串中的所有字符作为候选者,
只不过通过判断是否重复等方式来从众多候选者中过滤出符合条件的
回溯法模板:
成功则输出结果
设置变量
递归
清除变量
关键:
1 书上解法
固定第一个字符a,对后面的字符bc进行全排列
然后交换第一个字符与其后面的字符,即交换ab,然后固定第一个字符b,
对后面的两个字符ac进行全排列
再次交换a与b(因为刚才破坏了字符串顺序)
交换ac,固定此时第一个字符c,对ab进行全排列
2 关于重复:
如果前面的字符串中从[start, i)的字符串中有和当前
待交换重复的元素,则必定带来重复的全排列,
所以解决方法是:
如果string[start, i)字符串中有字符与string[i]
相同则不交换
3 实现
设定一个变量start表示起始字符
设定一个变量i表示后续交换的位置,i的取值从start 到 len(字符串) - 1
此时交换的字符索引
start不断累加,表示不断固定新的位置,当start的值为len(字符串) - 1,
说明完成
4 之所以没想到
是因为忘记可以通过固定前一个字符,求后续字符串的全排列思想,
另外判重的概念理解不清,应该是string[start, i)中只要有字符
与string[i]相同,就会造成全排列重复。
参考:
Python程序员面试算法宝典
'''
def strPermutationWrong(string, results, index, size):
# 找到,可以输出结果
if index == size:
print "".join(results)
return
# 否则将当前字符加入到结果数组
char = string[index] if index < size else None
results.append(char)
strPermutationWrong(string, results, index + 1, size)
# 回溯,清除刚才加入的元素
results.pop()
# 用于获取每个字符出现的次数,作为后面全排列算法中过滤候选字符
def getCharDict(string):
result = dict()
if not string:
return result
for char in string:
if char not in result:
result[char] = 1
else:
result[char] += 1
return result
def isOk(results, pos, posChar, charDict):
# 当前待摆放元素出现次数肯定是1,所以count从1而不是0开始
count = 1
limitTimes = charDict[posChar]
for i in range(pos):
char = results[i]
if char == posChar:
count += 1
if count > limitTimes:
return False
return True
def strPermutationWrong2(string, results, pos, size, charDict):
if pos == size:
print "".join(results)
return
# 每次选择的候选集都来自于整个字符串,只不过通过判重来过滤出下一个摆放的字符
for char in string:
flag = isOk(results, pos, char, charDict)
# 如果第pos个位置可以摆放字符char,递归进行下一次摆放
if flag:
results[pos] = char
strPermutationWrong2(string, results, pos + 1, size, charDict)
def swap(charList, start, i):
size = len(charList)
if start >= size or i >= size or start < 0 or i < 0:
return
tmp = charList[start]
charList[start] = charList[i]
charList[i] = tmp
def isDuplicated(charList, start, i):
size = len(charList)
if start >= size or i >= size or start < 0 or i < 0:
return True
for index in range(start, i):
if charList[index] == charList[i]:
return True
return False
def strPermutation(charList, start):
if not charList or start < 0:
return
size = len(charList)
if start == size - 1:
print "".join(charList)
i = start
while i < size:
# 判断待交换的两个元素是相同的,就不需要交换
if isDuplicated(charList, start, i):
i += 1
continue
swap(charList, start, i)
# 固定一个字符,求剩余字符的全排列
strPermutation(charList, start + 1)
# 恢复字符串顺序
swap(charList, start, i)
# 重新设定后续的交换字符
i += 1
def process():
string = "abc"
charList = list(string)
start = 0
strPermutation(charList, start)
print "#################"
string = "abb"
charList = list(string)
start = 0
strPermutation(charList, start)
def process2():
string = "abc"
index = 0
size = len(string)
charDict = getCharDict(string)
results = ['' for value in range(size)]
strPermutation(string, results, index, size, charDict)
print "###############"
string = "aba"
index = 0
size = len(string)
charDict = getCharDict(string)
results = ['' for value in range(size)]
strPermutation(string, results, index, size, charDict)
print "###############"
string = "aaa"
index = 0
size = len(string)
charDict = getCharDict(string)
results = ['' for value in range(size)]
strPermutation(string, results, index, size, charDict)
if __name__ == "__main__":
process()