Python程序员面试算法宝典---解题总结: 第5章 字符串 5.13 如何找到由其他单词组成的最长单词

# -*- 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()

猜你喜欢

转载自blog.csdn.net/qingyuanluofeng/article/details/94764347