返回

华为OD机试 - 最长子字符串的长度(二)(Java & JS & Python & C)之题解与实践

前端

解决华为OD机试:寻找最长有效子字符串

简介

华为OD机试平台上的“最长子字符串的长度(二)”问题是一个经典的问题,要求我们找出给定字符串中长度最长的有效子字符串。本文将探讨解决此问题的有效方法,使用前缀和、状态压缩和哈希表,并提供详细的代码示例和示例解释。

什么是有效子字符串?

有效子字符串是指字符串中所有字母出现次数相同的子字符串。例如,在字符串“abcabcabc”中,“abc”是一个有效子字符串,因为字符串中a、b、c的出现次数都为2。

解决方案

前缀和:

前缀和记录了每个字符在字符串中出现的次数。对于字符c,我们使用数组pre[c]记录c在字符串中从左到右出现的次数。

状态压缩:

为了优化算法,我们使用状态压缩减少需要考虑的状态数量。对于一个子字符串,我们可以用一个整数表示其所有字符出现的次数,从而减少了需要考虑的状态空间。

哈希表:

我们使用哈希表存储已考虑过的状态。这样,当我们遇到新状态时,我们可以通过哈希表判断它是否已经存在。如果存在,我们可以跳过该状态,因为它不可能产生更长的有效子字符串。

算法步骤

  1. 计算每个字符的前缀和pre。
  2. 使用状态压缩优化状态空间。
  3. 使用哈希表存储已考虑的状态。
  4. 对于字符串中的每个子字符串[l, r]:
    • 计算子字符串[l, r]的状态。
    • 如果状态已在哈希表中,跳过该状态。
    • 否则,将状态插入哈希表。
    • 更新最长有效子字符串的长度。

代码示例

Java

import java.util.*;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        String s = sc.next();
        int[] pre = new int[26];
        for (int i = 0; i < n; i++) {
            pre[s.charAt(i) - 'a']++;
        }
        int maxLen = 0;
        int state = 0;
        Map<Integer, Integer> map = new HashMap<>();
        for (int l = 0, r = 0; r < n; r++) {
            state ^= (1 << (pre[s.charAt(r) - 'a']));
            if (map.containsKey(state)) {
                l = Math.max(l, map.get(state) + 1);
            }
            map.put(state, r);
            maxLen = Math.max(maxLen, r - l + 1);
        }
        System.out.println(maxLen);
    }
}

Python

n = int(input())
s = input()
pre = [0] * 26
for i in range(n):
    pre[ord(s[i]) - ord('a')] += 1
maxLen = 0
state = 0
map = {}
l = 0
r = 0
while r < n:
    state ^= (1 << (pre[ord(s[r]) - ord('a')]))
    if state in map:
        l = max(l, map[state] + 1)
    map[state] = r
    maxLen = max(maxLen, r - l + 1)
    r += 1
print(maxLen)

示例解释

对于字符串“abcabcabc”,我们将计算每个字符的前缀和pre:

pre[a] = [1, 2, 3, 4, 5, 6, 7]
pre[b] = [0, 1, 2, 3, 4, 5, 6]
pre[c] = [0, 0, 1, 2, 3, 4, 5]

我们从状态0开始,并检查字符串中的每个子字符串:

  • 子字符串[0, 2]: 状态为1,因为a和b都出现了1次。将(1, 2)插入哈希表。
  • 子字符串[0, 3]: 状态为3,因为a、b和c都出现了1次。将(3, 3)插入哈希表。
  • 子字符串[0, 4]: 状态为7,因为a、b和c都出现了2次。将(7, 4)插入哈希表。

在[0, 4]之后,我们遇到(7, 4)再次出现,表明我们已经考虑过该状态。因此,我们跳过该状态并更新最长有效子字符串的长度为4(从[0, 3])。

常见问题解答

1. 为什么使用前缀和?

前缀和使我们能够高效地计算子字符串中每个字符的出现次数。

2. 状态压缩如何优化算法?

状态压缩通过将所有字符的出现次数压缩成一个整数来减少需要考虑的状态数量,从而提高了效率。

3. 哈希表如何帮助解决问题?

哈希表防止我们重复考虑相同的状态,因为相同状态对应于相同的哈希值。

4. 算法的时间复杂度是多少?

算法的时间复杂度为O(n),其中n是字符串的长度。

5. 除了最长有效子字符串,算法还可以解决哪些问题?

算法还可以用于解决查找最长不含重复字符的子字符串等类似问题。

总结

我们探索了使用前缀和、状态压缩和哈希表解决华为OD机试“最长子字符串的长度(二)”问题的有效方法。该解决方案高效且易于理解,对于解决此类问题很有用。