You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化满足特定条件的有效子串计数算法的时间复杂度?

问题需求

给定长度为n的小写英文字符串,需统计满足以下两个条件的子串数量:

  • 子串长度为偶数;
  • 子串中存在某个字符的出现次数等于子串长度的一半。

示例
输入字符串s="idafddfii",输出结果为13。
解释
符合条件的子串为:"id", "da", "af", "fd", "df", "fi", "dafd", "afdd", "fddf", "ddfi", "dfii", "idafdd", "dafddf"

约束条件

  • 1 ≤ n ≤ 10^5;
  • 字符串仅由小写英文字母组成。
现有实现及问题

当前Java实现的时间复杂度为O(n²),无法高效处理大数据量,需优化至更低时间复杂度:

public class Main {

    public static long solve(String s) {
        int n = s.length();
        long result = 0;
    
        for (int i = 0; i < n; i++) {
            int[] freq = new int[26];
            for (int j = i; j < n; j++) {
                freq[s.charAt(j) - 'a']++;
                int len = j - i + 1;
                // Only check even-length substrings
                if (len % 2 == 0) {
                    if (isValid(freq, len)) {
                        result++;
                    }
                }
            }
        }
        return result;
    }
    
    private static boolean isValid(int[] freq, int len) {
        int half = len / 2;
        for (int count : freq) {
            if (count == half) {
                return true;
            }
        }
        return false;
    }
    
    public static void main(String[] args) {
        String s1 = "aaaaid";
        String s2 = "aidfg";
        String s3 = "ababbab";
    
        System.out.println(solve(s1)); // Output: 3
        System.out.println(solve(s2)); // Output: 4
        System.out.println(solve(s3)); // Output: 8
    }

}
尝试进展与困惑

已参考建议尝试构建字符的累积频率数组,但不知后续如何利用该数组完成求解,代码如下:

import java.util.*;
public class Main {

    public static int solve(String s) {
        int n = s.length();
        int result = 0;
        Map<Character, int[]> map = new HashMap<>();
        for(int i=0; i<n; i++) {
            char ch = s.charAt(i);
            int[] cnt = map.getOrDefault(ch, new int[n]);
            cnt[i] += i == 0 ? 1 : cnt[i-1]+1;
            map.put(ch, cnt);
        }
        for(char c : map.keySet()) {
            System.out.println(c + ":" + Arrays.toString(map.get(c)));
        } 
        // what to do next
        return result;
    }

    public static void main(String[] args) {
        String s = "idafddfii";
        int output = solve(s);
        System.out.println(output); // Output: 13
    }
}

恳请提供将算法优化至更低时间复杂度的正确方法,以及如何利用累积频率数组完成求解。

优化方案

核心思路

我们可以把问题转化为:对每个字符c,统计所有偶数长度子串中c的出现次数恰好等于子串长度一半的数量,最后去重(避免子串因多个字符满足条件被重复计数)。关键是利用前缀频率推导的等式+哈希表来快速统计:

对于子串s[i+1..j](长度为j-i,偶数),设prefix[k][c]为前k个字符中c的出现次数,条件可转化为:
prefix[j][c] - prefix[i][c] = (j-i)/2
整理后得到:
2*prefix[j][c] - j = 2*prefix[i][c] - i

这意味着,对每个位置j,只需统计之前有多少个位置i满足上述等式,就能得到以j结尾、符合条件的子串数量(针对字符c)。

具体实现步骤

  1. 针对单个字符统计:
    对每个字符c,用哈希表记录2*prefix[i][c]-i的出现次数,遍历字符串时计算当前位置的对应值,累加哈希表中已有该值的次数,再更新哈希表。
  2. 去重处理:
    部分子串会同时满足多个字符的条件(比如"abba"中a和b的出现次数均为2),这类子串会被重复统计,需要单独计算重复数量并从总数中减去。

优化后的Java代码

import java.util.HashMap;
import java.util.Map;

public class Main {
    public static long solve(String s) {
        int n = s.length();
        long total = 0;

        // 统计每个字符对应的符合条件的子串数
        for (char target = 'a'; target <= 'z'; target++) {
            Map<Long, Integer> countMap = new HashMap<>();
            countMap.put(0L, 1);
            long currentPrefix = 0;
            for (int j = 1; j <= n; j++) {
                if (s.charAt(j - 1) == target) {
                    currentPrefix++;
                }
                long key = 2 * currentPrefix - j;
                total += countMap.getOrDefault(key, 0);
                countMap.put(key, countMap.getOrDefault(key, 0) + 1);
            }
        }

        // 减去重复统计的子串:由两个字符组成且各占一半次数的子串
        long duplicate = 0;
        for (char c1 = 'a'; c1 <= 'z'; c1++) {
            for (char c2 = c1 + 1; c2 <= 'z'; c2++) {
                Map<Long, Integer> map = new HashMap<>();
                map.put(0L, 1);
                long currentDiff = 0;
                for (int j = 1; j <= n; j++) {
                    char ch = s.charAt(j - 1);
                    if (ch == c1) {
                        currentDiff++;
                    } else if (ch == c2) {
                        currentDiff--;
                    }
                    duplicate += map.getOrDefault(currentDiff, 0);
                    map.put(currentDiff, map.getOrDefault(currentDiff, 0) + 1);
                }
            }
        }

        return total - duplicate;
    }

    public static void main(String[] args) {
        String s1 = "aaaaid";
        String s2 = "aidfg";
        String s3 = "ababbab";
        String s4 = "idafddfii";

        System.out.println(solve(s1)); // Output: 3
        System.out.println(solve(s2)); // Output: 4
        System.out.println(solve(s3)); // Output: 8
        System.out.println(solve(s4)); // Output: 13
    }
}

复杂度分析

  • 时间复杂度:O(26n + 26²n) = O(n),26为常数,完全适配n≤1e5的场景;
  • 空间复杂度:O(n),每个哈希表最多存储n个键值对。

内容的提问来源于stack exchange,提问作者CodeCrusader

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 13:22:00