📌  相关文章
📜  计算由前M个自然数组成的N长度数组,其子数组可以通过替换少于一半的元素而成为回文的(1)

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

计算回文子数组

问题描述

给定两个正整数 $N$ 和 $M$,请计算由前 $M$ 个自然数组成的长度为 $N$ 的数组中,子数组可以通过替换少于一半的元素而成为回文的方案数。其中,一个长度为 $K$ 的数组 $A$ 是回文数组,当且仅当对于所有的 $i \in [1, \lfloor K/2 \rfloor]$,有 $A_i = A_{K-i+1}$。

解题思路

首先我们考虑如何判断一个长度为 $K$ 的数组 $A$ 是否可以通过替换少于一半的元素而成为回文的。一种比较容易想到的做法是双指针,用一个指针从数组的开头向后扫描,另一个指针从数组的结尾向前扫描,每次比较两个指针所指向的元素是否相等,直到两个指针相遇。如果这个过程中不同元素的个数小于 $\lfloor K/2 \rfloor$,那么就可以通过替换少于一半的元素使得这个数组变成回文的。

接下来我们考虑如何枚举所有长度为 $N$ 的子数组,并对每个子数组应用上述的做法。一种比较朴素的方法是枚举所有长度为 $N$ 的子数组,并对每个子数组进行判断。由于 $N$ 的范围很大,到达 $10^3$ 或 $10^4$ 时就已经不能接受了。

我们发现,只有以某个位置为中心的长度为奇数的子串和长度为偶数的子串才有可能是回文的。因此,我们可以枚举所有可能的回文串,并检查这个回文串的覆盖范围是否在前 $M$ 个自然数范围内。具体来说,对于任意 $k \in [1, N]$,以 $k$ 为中心的奇数长度的回文串的覆盖范围是 $[k - (L-1)/2, k + (L-1)/2]$,其中 $L$ 是回文串的长度,以 $k$ 为中心的偶数长度的回文串的覆盖范围是 $[k - L/2 + 1, k + L/2]$,可以验证这些规律。

有了回文串的列表后,我们通过容斥原理计算出可以通过替换少于一半元素变成回文的子串个数。具体来说,对于任意一个回文串,其可以被替换元素的数量有两种情况,一种是替换回文串中心的那个元素,此时可以替换 $\lfloor L/2 \rfloor$ 个元素;另一种是不替换回文串中心的元素,此时可以替换 $\lfloor (L-1)/2 \rfloor$ 个元素。考虑所有回文串被替换元素数目之和的奇偶性,即可计算出可以通过替换少于一半元素变成回文的子串个数。

代码实现
def count_palindromic_subarrays(N, M):
    ans = 0
    for k in range(1, N+1):
        # odd-length palindromic substrings
        for L in range(1, min(k, N-k+1)+1, 2):
            left = k - (L-1) // 2
            right = k + (L-1) // 2
            if right <= M:
                ans += L // 2 + 1
            elif left > M:
                break
            else:
                ans += (M - left) // 2 + 1 if L % 2 == 1 else (M - left + 1) // 2
        # even-length palindromic substrings
        for L in range(2, min(k, N-k+2)+1, 2):
            left = k - L // 2 + 1
            right = k + L // 2
            if right <= M:
                ans += L // 2 + 1
            elif left > M:
                break
            else:
                ans += (M - left + 1) // 2
    return ans

时间复杂度 $O(N^2)$,可以通过本题。