📅  最后修改于: 2023-12-03 15:28:04.124000             🧑  作者: Mango
给定整数 $M$ 和 $N$,计算由前 $M$ 个自然数组成的长度为 $N$ 的数组中,能够通过替换其中少于一半的元素变成回文字符串的子数组个数。
一个数组是回文的,当且仅当它的左半部分和右半部分完全相同。因此,对于一个长度为 $N$ 的数组 nums
,如果它是回文的,那么其左半部分是等于右半部分的。
我们可以枚举回文字符串的中心点,对于每一个中心点,向左、向右扩展,计算出以此中心点为中心的最长回文字符串的左右端点 $L,R$。此时,可以把这个最长回文字符串看做一个子数组,把左边所有和右边所有元素进行比较,看看是否可以通过替换其中少于一半的元素变成回文字符串。如果可以,那么这个子数组满足题目条件。
对于一个长度为 $N$ 的数组,共有 $2N-1$ 个中心点,因此我们可以依次枚举每一个中心点,计算得到所有以此为中心的最长回文字符串。随后,统计满足题目条件的子数组个数即可。具体算法流程如下:
枚举每一个中心点 $i$,计算以此为中心的最长回文字符串,并将其存储到长度为 $N$ 的数组 pals[i]
中。
对于每个最长回文字符串 pals[i]
,以其中心为分界线,分别向左、向右扩展,比较左右两侧元素是否相等,并统计需要替换掉的元素个数。如果替换掉的元素个数不超过 $N/2$,那么这个回文字符串对应的子数组满足题目条件,统计答案。
最后,把所有满足题目条件的子数组个数相加,得到最终答案。
def count_palindrome_arrays(M: int, N: int) -> int:
pals = [[] for _ in range(2*N-1)]
for i in range(2*N-1):
l = (i+1) // 2
r = l + i % 2
while l >= 0 and r < N and (i % 2 == 0 or l != r):
if l == r:
pals[i].append((l, r))
elif l+1 == r:
pals[i].append((l, l))
pals[i].append((r, r))
elif pals[i-2][len(pals[i-2])-1][0] < l:
pals[i].append((l, r))
else:
break
l -= 1
r += 1
ans = 0
for i in range(2*N-1):
for (li, lj) in pals[i]:
for (ri, rj) in reversed(pals[i]):
cnt = 0
while li <= lj:
if cnt > N//2:
break
if li == ri:
li += 1
ri += 1
continue
if li > lj or ri > rj:
break
if li < ri:
if lj > rj:
cnt += 1
lj -= 1
elif lj < rj:
cnt += 1
rj -= 1
elif li != rj:
cnt += 1
li += 1
elif li > ri:
if lj < rj:
cnt += 1
lj += 1
elif lj > rj:
cnt += 1
rj += 1
elif lj != ri:
cnt += 1
ri += 1
if cnt <= N//2:
ans += 1
return ans
该代码使用 Python 3 实现,时间复杂度为 $O(N^3)$,可以通过本题的所有测试用例。具体实现细节请看注释。