leetcode
leetcode 2551 ~ 2600
同端子串的数量

同端子串的数量

难度:

标签:

题目描述

代码结果

运行时间: 799 ms, 内存: 33.5 MB


/**
 * Problem: Leetcode 2955 - Count Equal Substrings
 *
 * Idea: Using Java Streams, we can achieve the same goal by creating a stream
 * of all possible substrings and then filtering those that have the same beginning
 * and ending characters. This solution leverages streams and lambda expressions,
 * providing a more functional approach.
 */

import java.util.stream.IntStream;

public class CountEqualSubstringsStream {
    public long countEqualSubstrings(String s) {
        int n = s.length();

        return IntStream.range(0, n)
                .mapToLong(i -> IntStream.range(i, n)
                        .filter(j -> s.charAt(i) == s.charAt(j))
                        .count())
                .sum();
    }

    public static void main(String[] args) {
        CountEqualSubstringsStream cess = new CountEqualSubstringsStream();
        String s = "abcab";
        System.out.println(cess.countEqualSubstrings(s)); // Output should be 7
    }
}

解释

方法:

此题解采用前缀和数组来快速计算字符串中任意子串的字符频率。首先,构建一个前缀和数组counters,其中counters[i][j]表示字符串s中前i个字符中字符('a'+j)出现的次数。接着,对于每个查询,我们可以快速得到子串s[x...y]中每个字符的出现次数,然后计算每个字符能形成的同端子串的数量,即字符出现次数乘以该次数加一再除以二。最后,将这些数相加得到对应查询的结果。

时间复杂度:

O(n + q)

空间复杂度:

O(n)

代码细节讲解

🦆
在构建前缀和数组时,你是如何确保每个字符的计数是准确的?是否有可能出现计数误差?
在构建前缀和数组时,每次我们遍历字符串s的一个字符,都会复制上一个前缀数组并对当前字符的计数进行更新。这个更新是单步、直接的加法操作,因此只要程序逻辑正确,就不会出现计数误差。我们从第一个字符到最后一个字符一次处理,保证每一步的计数都是建立在前一步准确结果的基础上的。
🦆
前缀和数组counters的初始化为什么选择[[0]*26],这里代表的意义是什么?
前缀和数组counters的初始化为[[0]*26]表示在字符串s的开始前,即位置0之前,所有26个英文字母的出现次数都是0。这是为了确保在计算字符串任意前缀的字符频率时,我们可以直接使用counters数组而无需考虑边界条件。例如,要计算从字符串开始到某位置y的某字符的频率,可以直接查看counters[y+1]。
🦆
在处理查询时,counters[y+1][i] - counters[x][i]的计算方式是如何确保能够准确得到子串s[x...y]中每个字符的计数的?
前缀和数组通过counters[y+1][i]表示从字符串开始到位置y(包括y)的字符i的出现次数,而counters[x][i]表示从字符串开始到位置x-1的字符i的出现次数。因此,counters[y+1][i] - counters[x][i]正好给出了从位置x到位置y的子串中字符i的出现次数。这个计算方法是基于前缀和的性质,确保了计算的准确性。
🦆
为什么每个字符能形成的同端子串的数量可以用公式v * (v + 1) // 2来计算?这里的逻辑基础是什么?
当一个字符在子串中出现v次时,可以形成从1到v的所有可能的子串长度的同端子串。例如,字符连续出现3次,可以形成长度为1的子串3个,长度为2的子串2个,长度为3的子串1个,总共6个。这可以通过求和公式v*(v+1)/2来计算,该公式实际上是计算从1加到v的自然数的和,每个自然数代表该长度的同端子串的数量。

相关问题