回文子串查询

2025年3月17日 | 阅读 7 分钟

给定一个字符串和一组查询,这些查询指定了包含首尾的范围,需要判断指定范围内的子串是否为回文。

考虑以下场景:我们有一个输入字符串 "abaaabaaaba" 和一组查询:[0, 10], [5, 8], [2, 5], [5, 9]。我们的任务是确定这些查询索引指定的子字符串是否为回文。

例如

对于长度为 N 的输入字符串,解决 Q 个此类查询有两种主要方法:

方法1(朴素方法)

在这种方法中,我们单独遍历每个查询,并检查指定的子字符串是否为回文。由于有 Q 个此类查询,并且在最坏情况下,每个查询判断回文可能需要 O(N) 的时间,因此此方法的最坏时间复杂度为 O(Q * N)。虽然此方法节省空间,但存在更有效的替代方案。

该方法的 Java 实现

输出

Palindrome substring queries

时间复杂度

检查每个子字符串的回文特性涉及比较左右两边的字符。

对于每个长度为 N 的子字符串的查询,时间复杂度为 O(N)。

如果总共有 Q 个查询,则整体时间复杂度为 O(Q * N),其中 Q 是给定问题中的查询数量,N 是子字符串的最大长度。

空间复杂度

空间复杂度为 O(1),因为程序使用的额外内存量不依赖于输入大小。无论输入字符串长度或查询数量如何,它只使用常量内存用于变量和数据结构。

方法2(累积哈希)

这种方法背后的概念类似于 Rabin-Karp 字符串匹配算法。它依赖于字符串哈希,涉及计算原始字符串和反转字符串的累积哈希值,并将它们存储在两个数组中:`prefix[]` 和 `suffix[]`。

溢出问题

即使对于小字符串,哈希值也可能变得非常大。

为避免溢出,代码对所有操作执行模 1000000007,这是一个出于数学原因选择的素数。这个素数适合整数值。

Java 和 Python 可以在没有模运算的情况下处理这些大值。

程序中使用的主要模运算是:

加法:(a + b) % M = (a % M + b % M) % M

乘法:(a * b) % M = (a * b) % M

加法和乘法混合:(a * x + b * y + c) % M = ((a * x) % M + (b * y) % M + c % M) % M

减法:(a - b) % M = (a % M - b % M + M) % M

除法:(a / b) % M = (a * MMI(b)) % M,其中 MMI() 计算模乘法逆元,由程序中的 `findMMI()` 函数实现。

Java 实现

输出

Palindrome substring queries

工作方式

输入

字符串:"abbaaba"

查询:[0, 6], [1, 4], [2, 5], [0, 3]

步骤1:常量和辅助函数

BASE 设置为 101。

MODULO 设置为 1000000007。

步骤2:预计算 BASE 的幂

我们预计算一个 BASE 幂的数组,以实现高效哈希。对于此示例,假设 `powersOfBase` 是 [1, 101, 101^2, ..., 101^6]。

步骤3:计算前缀和后缀哈希

我们计算两个数组:

`prefixHash`:存储原始字符串 "abbaaba" 的哈希值。

`suffixHash`:存储反转字符串 "abaabba" 的哈希值。

假设这些数组填充如下:

`prefixHash`:[0, 97, 10098, ..., HashValueAtIndexN]

`suffixHash`:[0, 97, 10098, ..., HashValueAtIndexN]

这些数组是使用模算术和 BASE 的幂计算的。

步骤4:处理查询

我们有四个查询,我们希望确定指定的子字符串是否为回文。

查询1:[0, 6]

使用 `prefixHash` 和 `powersOfBase` 计算子字符串 "abbaaba" 的哈希值。

使用 `suffixHash` 和 `powersOfBase` 计算反转子字符串 "abaabba" 的哈希值。

比较哈希值。如果它们匹配,则使用 `isPalindrome` 比较字符。在此情况下,它们匹配,因此它是回文。

查询2:[1, 4]

使用 `prefixHash` 和 `powersOfBase` 计算子字符串 "bbaa" 的哈希值。

使用 `suffixHash` 和 `powersOfBase` 计算反转子字符串 "aabb" 的哈希值。

比较哈希值。它们不匹配,因此它不是回文。

查询3:[2, 5]

类似于查询2,计算子字符串 "baab" 及其反转的哈希值。

同样,哈希值不匹配,因此它不是回文。

查询4:[0, 3]

使用 `prefixHash` 和 `powersOfBase` 计算子字符串 "abba" 的哈希值。

使用 `suffixHash` 和 `powersOfBase` 计算反转子字符串 "abba" 的哈希值。

哈希值匹配,并且 `isPalindrome` 确认它是回文。

时间复杂度: O(n*m)

空间复杂度:O(n)


下一主题Recaman序列