📜  长度为 2 或更长的重复子序列(1)

📅  最后修改于: 2023-12-03 15:28:36.007000             🧑  作者: Mango

长度为 2 或更长的重复子序列

什么是重复子序列?

重复子序列是指在一个字符串中,长度至少为2的两个子串在原字符串中出现了多次。

例如,字符串“ABABC”中,“AB”是一个重复子序列,在位置1和位置3分别出现了一次。另一个重复子序列为“AA”,在位置1和位置2分别出现了一次。需要注意的是,“A”虽然在字符串中出现了多次,但是它并不是一个重复子序列,因为它的长度为1。

如何判断字符串中是否存在重复子序列?

我们可以使用哈希表来判断字符串中是否存在重复子序列。对于每一个长度大于等于2的连续子串,在哈希表中记录其出现的起始位置。当我们扫描到一个新的子串时,首先计算它的哈希值,然后检查哈希表中是否已经存在该哈希值。如果存在,说明该子串曾经出现过,那么我们就可以找到它和当前子串的起始位置,从而判断它们是否完全相同。

代码实现如下所示:

def find_repeated_substring(s: str) -> str:
    n = len(s)
    hash_map = {}
    for i in range(2, n + 1):
        for j in range(n - i + 1):
            sub_str = s[j:j + i]
            hash_value = hash(sub_str)
            if hash_value in hash_map and hash_map[hash_value] != j:
                return sub_str
            hash_map[hash_value] = j
    return ""

注意,在哈希表中我们需要记录子串出现的起始位置,因为存在相同的哈希值对应多个不同的子串。

如何找到所有的重复子序列?

如果我们需要找到字符串中所有的重复子序列,可以使用字符串算法中的后缀数组和公共前缀数组。

后缀数组的定义是:给定一个字符串S,将S的所有后缀按字典序排序后所得到的数组。例如,字符串“abcd”的后缀数组为[‘abcd’, ‘bcd’, ‘cd’, ‘d’]。

公共前缀数组的定义是:给定一个字符串S和它的后缀数组suffix,令LCP[i]表示suffix[i]和suffix[i-1]的最长公共前缀的长度。

假设我们已经得到了字符串的后缀数组和公共前缀数组,那么我们可以依次遍历后缀数组中的每一个后缀,找到它和前一个后缀的最长公共前缀。如果它们的最长公共前缀长度大于等于2,那么它们就是一个重复子序列。

注意,如果后缀数组中有重复的后缀,那么它们对应的公共前缀数组中的值也会是重复的。因此,我们需要去重处理,只保留其中一个。

代码实现如下所示:

def find_all_repeated_substrings(s: str) -> List[str]:
    n = len(s)
    suffix_array = sorted(range(n), key=lambda i: s[i:])
    LCP = [0] * n
    for i in range(1, n):
        LCP[suffix_array[i]] = lcp(s, suffix_array[i-1], suffix_array[i])
    res = set()
    for i in range(1, n):
        if LCP[i] >= 2:
            res.add(s[suffix_array[i]: suffix_array[i] + LCP[i]])
    return list(res)

def lcp(s: str, i: int, j: int) -> int:
    n = len(s)
    k = 0
    while i < n and j < n and s[i] == s[j]:
        i += 1
        j += 1
        k += 1
    return k

注意,这里使用了Python内置的set来进行去重处理,因此返回的结果是一个列表,而不是一个集合。此外,我们还需要实现一个lcp函数来计算任意两个后缀的最长公共前缀长度。