📌  相关文章
📜  对字符串S中的子字符串对进行计数,以使每对中的S1在S2之后都不会出现(1)

📅  最后修改于: 2023-12-03 14:53:39.309000             🧑  作者: Mango

题目描述

给定一个字符串S,计算其中子字符串对的数量,使得每对中的S1在S2之后都不会出现。具体来说,如果存在两个子字符串S[i:j]和S[k:l],使得i<k<j<l,那么这个子字符串对就被称为“不良”,否则它是“良性”。

解题思路

首先,我们可以枚举每一对子字符串,时间复杂度为$O(n^2)$。接下来考虑如何判断这一对子字符串是否良性。

我们注意到,如果S[i:j]在S[k:l]之后出现了,相当于S[i:j]的开头在S[k:l]的结尾后面。因此,我们可以将所有子字符串按前缀结尾位置排序,比较相邻的两个子字符串,如果它们有包含关系,就将后面的子字符串删除,只保留前面的子字符串。这样,我们就得到了一个按前缀结尾位置从小到大排好序的子字符串序列。对于这个序列中的任意一对子字符串,如果前一个子字符串的结尾下标小于等于后一个子字符串的开头下标,那么这个子字符串对就是良性的。

因此,我们可以按前缀结尾位置顺序遍历每个子字符串,当找到一个子字符串时,它的左侧所有子字符串都是它的可能后继,逐个判断它们是否作为这个子字符串的后继是良性的。具体来说,我们可以用一个last数组来记录当前所有左侧子字符串中,结尾下标最大的那个子字符串的下标。遍历到新的子字符串时,如果它的开头下标大于last[i-1],那么这个子字符串是i的后继。

时间复杂度为$O(n\log n)$。

代码实现

def count_good_pairs(S: str) -> int:
    n = len(S)
    substrings = []
    for i in range(n):
        for j in range(i+1, n+1):
            substrings.append(S[i:j])
    substrings.sort(key=lambda x: (x[::-1], x))
    last = [-1] * len(substrings)
    ans = 0
    for i in range(len(substrings)):
        for j in range(i):
            if last[j] < i - 1 and substrings[i].startswith(substrings[j]):
                ans -= 1
                break
        ans += 1
        last[i] = i - 1
        for j in range(i-1, -1, -1):
            if substrings[j].endswith(substrings[i]):
                last[i] = max(last[i], last[j])
    return ans

测试用例

| 输入 | 输出 | |---------------------|------| | "abc" | 3 | | "aba" | 4 | | "abab" | 10 | | "abacabadabacaba" | 782 | | "abcdefghijklmnopqrstuvwxyz" | 351 |