# -*- coding: utf-8 -*-
'''
Python程序员面试算法宝典---解题总结: 第5章 字符串 5.13 如何找到由其他单词组成的最长单词
题目:
给定一个字符串数组,找出数组中最长的字符串,使其能由数组中其他的字符串组成。
例如给定字符串数组
["test", "tester", "testertest", "testing", "apple",
"seattle", "banana", "batting", "ngcat", "batti",
"bat", "testingtester","testbattingcat"]。
满足题目要求的字符串为"testbattingcat",因为这个字符串
可以由数组中的字符串"test", "batti", "ngcat"组成。
分析:
组成说明既需要小字符串在大字符串中出现,也需要几个小字符串
恰好可以某种顺序拼接起来变成大字符串。
可否用trie树来做?
关键:
1 书上解法
看到"最长","最小","最优"就应该想到贪心算法,广度优先搜索算法,动态规划算法。
回溯一般是求所有情况,满足条件的所有解。
递归一般是求满足条件的一种解。
可以采用贪心算法,先对字符串数组按照从大到小的顺序排序。
然后先选取最长的字符串,判断其所有可能的前缀,
如果前缀在字符串数组中,则递归对剩余除去前缀的字符串进行处理。
如果剩余除去前缀的字符串的前缀没有在字符串数组中,
则返回上一层,重新选取一个新的前缀
2 我没想到是因为
1) 没想到是贪心处理,按照字符串长度从长到短排序
2) 忘记对于这种剩余字符串递归处理,可以直接用剩余字符串作为下一次递归
的参数。递归出口就是剩余字符串为空。
另外对于需要枚举各种前缀的情况,采用for循环中套递归实现。
模板如下:
if 满足条件:
return True
for ....:
如果符合条件:
递归处理
所有前缀都不符合,返回False
样式如下:
lens = len(string) if string else 0
# 递归结束条件,如果字符串长度为0,说明字符串已经遍历完成
if 0 == lens:
return True
for i in range(1, lens + 1):
# 如果取到的子串是自己,就返回False
if i == length:
return False
prefixStr = string[0:i]
if prefixStr in wordSet:
# 递归判断后续剩余字符串是否可以由其他字符串组成
result = find(string[i:], wordSet, length)
参考:
Python程序员面试算法宝典
'''
def compare(str1, str2):
if not str1 and not str2:
return 0
elif not str1:
return -1
elif not str2:
return 1
len1 = len(str1)
len2 = len(str2)
if len1 > len2:
return 1
elif len1 < len2:
return -1
else:
return 0
def partition(words, low, high):
if not words:
return
value = words[low]
while low < high:
while low < high and compare(words[high], value) <= 0:
high -= 1
words[low] = words[high]
while low < high and compare(words[low], value) >= 0:
low += 1
words[high] = words[low]
words[low] = value
return low
'''
按照字符串长度大小从大到小排序
'''
def quickSort(words, low, high):
if not words:
return
if low < high:
index = partition(words, low, high)
quickSort(words, low, index - 1)
quickSort(words, index + 1, high)
'''
输入参数:
num,当前待处理的字符串是words[num]
posBegin: 前缀字符串的起始位置
prefixLen: 前缀字符串的长度,string[posBegin: posBegin + suffixLen + 1] 作为string的前缀字符串
words: 字符串数组
wordSet: 字符串集合,用于判定前缀字符串是否在字符串集合中
处理逻辑:
从字符串最长的字符串开始处理,对该字符串
每次获取前缀(从短到长)字符串,判断前缀字符串
是否在单词集合中;
1如果在,对字符串中除去该前缀
字符串剩余部分进行处理。
2否则,表明当前前缀字符串不符合,寻找下一个前缀字符串
如何判断找到这样的字符串,剩余字符串作为一个前缀可以被
找到
'''
'''
判断字符串string是否能由其他单词构成
length是该字符串string的长度
'''
def find(string, wordSet, length):
lens = len(string) if string else 0
# 递归结束条件,如果字符串长度为0,说明字符串已经遍历完成
if 0 == lens:
return True
for i in range(1, lens + 1):
# 如果取到的子串是自己,就返回False
if i == length:
return False
prefixStr = string[0:i]
if prefixStr in wordSet:
# 递归判断后续剩余字符串是否可以由其他字符串组成
result = find(string[i:], wordSet, length)
if result:
return True
return False
def mainFind(words, wordSet):
# 遍历每个字符串,进行处理,一旦处理结果为True,表示找到
for word in words:
result = find(word, wordSet, len(word))
if result:
return word
return ""
def process():
words = ["test", "tester", "testertest", "testing", "apple",
"seattle", "banana", "batting", "ngcat", "batti",
"bat", "testingtester","testbattingcat"]
low = 0
high = len(words) - 1
quickSort(words, low, high)
print words
wordSet = set()
for value in words:
wordSet.add(value)
result = mainFind(words, wordSet)
print result
if __name__ == "__main__":
process()