📌  相关文章
📜  三元字符串中要替换的最少字符以删除 Q 查询的所有回文子字符串(1)

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

三元字符串中要替换的最少字符以删除 Q 查询的所有回文子字符串

简介

给定一个由小写字母a,b,c和大写字母Q组成的字符串s。你可以最多更改字符串中的三个字符。你需要将s转换为t,使得t没有回文子字符串(一个字符串是回文,当且仅当该字符串与其反转后的字符串相同)。还有一些查询Q,对于每个查询,你需要输出当前t中匹配查询的子字符串数量。

解法

首先,我们先将所有的回文子字符串都找出来,并放到列表中。然后,对于每个回文子字符串,我们可以删除其中一个字符,以避免它是回文的。由于最多可以更改三个字符,因此我们现在可以删除最多三个回文子字符串中的一个字符。我们可以预处理回文子字符串的需要删除的字符数量,并使用动态规划算法找到可以删除的最少字符数。

假设我们有一个数组dp[i][j],它表示从i到j所需的最少字符数。当s[i] == s[j]时,如果从i + 1到j - 1所需的最少字符数小于等于1,那么dp[i][j] = dp[i + 1][j - 1]。否则,我们需要删除s[i]或s[j],因此dp[i][j] = 1 + min(dp[i + 1][j], dp[i][j - 1])。

最终的答案是字符串长度减去可以删除的最少字符数。

如此一来,我们就可以回答查询。我们可以使用哈希表来记录t中出现的多少次字符串。对于每个查询,我们只需要在哈希表中查找该子字符串出现的次数即可。

实现

下面是一份Python代码,用于实现上述算法。

def count_palindromes(s):
    n = len(s)
    p = [[False] * n for _ in range(n)]
    for i in range(n):
        p[i][i] = True
    for i in range(n - 1):
        p[i][i + 1] = (s[i] == s[i + 1])
    for l in range(2, n):
        for i in range(n - l):
            j = i + l
            p[i][j] = p[i + 1][j - 1] and s[i] == s[j]
    
    dp = [[float('inf')] * n for _ in range(n)]
    for i in range(n):
        dp[i][i] = 0
    for i in range(n - 1):
        dp[i][i + 1] = 1 if s[i] != s[i + 1] else 0
    for l in range(2, n):
        for i in range(n - l):
            j = i + l
            if s[i] == s[j]:
                dp[i][j] = dp[i + 1][j - 1]
            else:
                dp[i][j] = 1 + min(dp[i + 1][j], dp[i][j - 1])
    
    ans = n - min(dp[0][n - 1], 3)
    
    counts = {}
    for i in range(n):
        for j in range(i + 1, n + 1):
            if p[i][j - 1]:
                t = s[i:j]
                if t in counts:
                    counts[t] += 1
                else:
                    counts[t] = 1
    
    return ans, counts

s = input()
t, counts = count_palindromes(s.replace('Q', ''))
for _ in range(int(input())):
    q = input()
    if q in counts:
        print(counts[q])
    else:
        print(0)
参考

LeetCode 1216. Valid Palindrome III