还原原始字符串 | 豆包MarsCode AI刷题

101 阅读4分钟

一、题目背景

本题要求给定一个字符串 F,通过若干次字符串操作推测出可能的初始字符串 S。该操作是将字符串 S 从某一位置 K 开始的子串追加到 S 的末尾,生成新的字符串。我们需要根据最终的字符串 F,推导出最初的字符串 S。如果无法通过这种操作生成 F,则返回原字符串 F

二、解题思路

从题目描述可以看出,初始字符串 S 通过反复的操作演化成最终的字符串 F。我们的目标是从 F 中推断出可能的初始字符串。为了实现这一目标,我们需要从短字符串开始尝试,通过模拟操作验证该字符串是否能通过操作生成最终字符串。

1. 操作模拟

给定初始字符串 S 和一个整数 K,字符串 S 从位置 K 开始的子串被追加到 S 的末尾,生成的新字符串为 S + S[K:]。例如,如果 S"ab"K = 1 时,S + S[1:] 变为 "ab" + "b",结果为 "abb"。我们通过这个操作不断生成新的字符串,直到生成目标字符串 F

2. 字符串匹配

对于给定的字符串 F,我们需要检查是否存在一个子串 S,使得我们通过若干次操作能够生成 F。为了避免暴力破解所有可能的情况,我们采用一种策略:

  1. F 中选取所有可能的前缀。
  2. 对每个前缀 S,模拟从 SF 的操作,判断是否能通过多次操作生成 F
  3. 如果能生成 F,就选取最短的 S
3. 广度优先搜索(BFS)

为了检查一个字符串 S 是否能通过若干次操作生成目标字符串 F,我们使用 BFS 来模拟操作过程。通过 BFS,我们能够探索每种可能的操作,并且只要一个操作能够生成目标字符串,就可以立刻返回结果。

三、具体实现

我们首先定义了一个辅助函数 can_generate(s, target),它检查给定字符串 s 是否能通过重复操作生成目标字符串 target。该函数使用队列(广度优先搜索)来模拟从 starget 的操作,若找到一个匹配的字符串,则返回 True,否则返回 False

其次,我们定义了 get_all_prefixes(s) 函数来生成所有可能的前缀,并验证每个前缀是否能通过操作生成最终的字符串。

最终,我们会返回最短的初始字符串。如果没有任何前缀能生成目标字符串,则直接返回原始字符串 F

四、代码实现

def solution(str1):
    def can_generate(s, target):
        """检查字符串s是否能通过操作生成目标字符串target"""
        current = s
        used = {current}
        q = [current]
        
        while q and len(q[0]) <= len(target):
            current = q.pop(0)
            if current == target:
                return True
                
            # 尝试所有可能的K
            for k in range(len(current)):
                next_str = current + current[k:]
                if len(next_str) <= len(target) and next_str not in used:
                    if target.startswith(next_str):
                        used.add(next_str)
                        q.append(next_str)
        return False

    def get_all_prefixes(s):
        """获取字符串的所有可能前缀"""
        result = []
        for i in range(1, len(s) + 1):
            prefix = s[:i]
            if can_generate(prefix, s):
                result.append(prefix)
        return result

    # 获取所有可能的前缀
    possible_prefixes = get_all_prefixes(str1)
    
    # 如果没有找到可能的前缀,返回原字符串
    if not possible_prefixes:
        return str1
        
    # 返回最短的可能前缀
    return min(possible_prefixes, key=len)

if __name__ == "__main__":
    test_cases = [
        "abbabbbabb",  # "ab"
        "abbbabbbb",  # "ab"
        "jiabanbananananiabanbananananbananananiabanbananananbananananbananananbanananan",  # "jiaban"
        "selectecttectelectecttectcttectselectecttectelectecttectcttectectelectecttectcttectectcttectectcttectectcttect",  # "select"
        "discussssscussssiscussssscussssdiscussssscussssiscussssscussssiscussssscussss",  # "discus"
        "lflsdjlskjfl"  # "lflsdjlskjfl"
    ]
    
    expected_results = [
        "ab",
        "ab",
        "jiaban",
        "select",
        "discus",
        "lflsdjlskjfl"
    ]
    
    for i, test_case in enumerate(test_cases):
        result = solution(test_case)
        print(f"Test case {i + 1}: {result == expected_results[i]}")

五、时间复杂度分析

在这道题目中,主要的时间消耗集中在两个方面:

  1. 获取所有可能前缀:我们对于每个可能的前缀都执行一次 can_generate 函数。每次 can_generate 需要遍历每个可能的操作,最坏情况下每次操作的时间复杂度为 O(n),因此,对于一个长度为 n 的字符串,最坏情况下会遍历 O(n^2) 次。
  2. 操作验证:对于每个前缀,我们需要通过 BFS 来模拟字符串操作。BFS 的时间复杂度是 O(n),因为最多需要遍历 n 次。

因此,整个程序的时间复杂度为 O(n^2),在题目给定的字符串长度限制下(最多 1000),这是一个可以接受的复杂度。

六、结论

本题通过模拟字符串的操作,借助广度优先搜索(BFS)来验证每个前缀是否能生成目标字符串。利用这种方式,我们能够高效地推导出可能的最短初始字符串。通过合理地使用队列和集合,可以避免重复计算并优化性能,解决了字符串构造的逆向问题。