Java C++ 算法leetcode828统计子串中唯一字符乘法原理

Java C++ 算法leetcode828统计子串中唯一字符乘法原理

题目描述

给定一个字符串,你需要统计其中唯一字符的个数。

具体地,你需要统计所有的出现恰好一次的字符的个数。

示例

输入: "ABCDEF"
输出: 6
解释: 
出现一次的字符有 'A', 'B', 'C', 'D', 'E', 'F',因此唯一字符的个数为 6。

输入: "ABCDEFABCDEF"
输出: 0
解释: 
都出现了两次的字符,因此没有唯一字符。

解题思路

这道题可以看成是对于任意子串求解唯一字符的个数,这个问题可以被下面的定理所解决:

对于任意的 $i\le j$,$\text{count(i,j)} = \text{count(1,j)} - \text{count(1,i-1)}$,其中 $\text{count(i,j)}$ 表示区间 $[i,j]$ 中唯一字符的个数。

证明如下:

  • 显然,当 $i=j$ 时,$\text{count(i,j)}=1$,因为这是一个单个字符。
  • 当 $i<j$ 时,我们需要考虑将何时出现重复字符:

    • 假设 $\text{count(1,j)} - \text{count(1,i-1)}=k$,表示区间 $[i,j]$ 中唯一字符的个数为 $k$,则代表区间 $[i,j]$ 中有 $j-i+1-k$ 个字符出现至少两次。
    • 我们接下来考虑新加入一个字符 $c$,并判断这个字符是否会让唯一字符的个数改变:

      • 如果 $c$ 之前在区间 $[i,j]$ 中尚未出现过,由于它是唯一字符,所以唯一字符个数加一。
      • 如果 $c$ 之前在区间 $[i,j]$ 中已经出现过了,那么此时该字符和之前的某个字符 $\hat{c}$ 都存在了,所以唯一字符个数就少了一个。
    • 因此,当我们加入新的 $c$ 时,唯一字符个数的变化可以通过判断 $c$ 是否在区间 $[i,j]$ 中出现来进行决策:

      • 如果 $c$ 在区间内未出现,那么唯一字符个数加一;
      • 如果 $c$ 在区间内已经出现,那么唯一字符个数减一。
    • 由此可以得到结论:区间 $[i+1,j]$ 中唯一字符个数是区间 $[i,j]$ 的唯一字符个数加零或减一。

因此,我们可以用滑动窗口来维护区间的唯一字符个数,每次增加一位字符,就按照定理来决定是否增加或减少唯一字符的个数即可。

代码示例

Java:

public int uniqueLetterString(String S) {
    if(S == null || S.length() == 0) {
        return 0;
    }
    int n = S.length();
    int[] lastIndex = new int[26];
    int[] contribution = new int[26];
    Arrays.fill(lastIndex, -1);
    long result = 0;
    int mod = 1000000007;
    for(int i = 0; i < n; i++) {
        int index = S.charAt(i) - 'A';
        if(lastIndex[index] == -1) {
            contribution[index] = (i + 1);
        } else {
            contribution[index] = (i - lastIndex[index]);
        }
        lastIndex[index] = i;
        result = (result + contribution[index]) % mod;
    }
    return (int)result;
}

C++:

class Solution {
public:
    int uniqueLetterString(string S) {
        if (S.empty()) {
            return 0;
        }
        int n = S.size();
        vector<int> lastIndex(26, -1), contribution(26, 0);
        long long result = 0;
        const int mod = 1e9 + 7;
        for(int i = 0; i < n; i++) {
            int index = S[i] - 'A';
            if(lastIndex[index] == -1) {
                contribution[index] = (i + 1);
            } else {
                contribution[index] = (i - lastIndex[index]);
            }
            lastIndex[index] = i;
            result = (result + contribution[index]) % mod;
        }
        return result;
    }
};

总结

这题用了一个很巧妙的思想。了解这个变换可以帮助我们快速求解子串的相关问题,这里推荐一下书籍:《算法竞赛进阶指南》。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java C++ 算法leetcode828统计子串中唯一字符乘法原理 - Python技术站

(0)
上一篇 2023年5月19日
下一篇 2023年5月19日

相关文章

  • Java实现Excel导入导出的步骤详解

    Java实现Excel导入导出的步骤详解 Excel导入导出在日常开发中非常常见,Java语言作为一种非常流行的开发语言,在Excel导入导出方面也提供了很好的支持,本文将为大家详细介绍Java实现Excel导入导出的步骤。 相关技术介绍 在Java语言中,常用的Excel导入导出技术有以下几种: POI技术:免费的Java API,可以新建表格,也可以读写…

    Java 2023年6月15日
    00
  • Java——对象初始化顺序使用详解

    Java——对象初始化顺序使用详解 在Java中,对象初始化的顺序非常重要,因为它直接影响程序的行为以及可能导致程序出现一些难以调试的错误。本文将详细讲解Java中对象初始化的顺序及其使用注意事项。 对象初始化顺序 当一个Java对象被创建时,其成员变量会被初始化为其对应的初始值。但是,如果类中包含了静态块、静态变量、实例块、实例变量、构造函数等初始化代码,…

    Java 2023年5月26日
    00
  • java map转Multipart/form-data类型body实例

    下面是java map转Multipart/form-data类型body的详细攻略: 创建一个MultiPart对象 在将Map类型转换成Multipart/form-data类型之前,我们需要先创建一个MultiPart对象作为容器,并传入Content-Type为multipart/form-data的Header。 MultiPart multiPa…

    Java 2023年5月20日
    00
  • Spring加载属性文件方式(自动加载优先级问题)

    Spring是一个非常流行的Java开发框架,它提供了丰富的配置选项和灵活的配置方式。其中属性文件的加载方式是Spring配置中的一个重要部分。本篇文章将详细介绍Spring加载属性文件的方式,以及自动加载优先级问题。 Spring加载属性文件方式 在Spring中,有多种方式可以加载属性文件: 使用PropertyPlaceholderConfigurer…

    Java 2023年6月15日
    00
  • 运行时数据区域包括哪些部分?

    以下是关于 Java 运行时数据区域的详细讲解: 运行时数据区域包括哪些部分? Java 的运行时数据区域是指 Java虚拟机(JVM)在运行 Java程序所使用的内存区域。Java 的运行时区域包括以下几个部分: 程序计数器(Program Counter Register):用于记录当前线程执行的字节令地址。 Java 虚拟机栈Java Virtual …

    Java 2023年5月12日
    00
  • 详解Java对象结构与对象锁的升级

    详解Java对象结构与对象锁的升级 Java对象结构 Java对象在内存中的实际存储由三部分组成:对象头、实例数据和对齐填充。 对象头 对象头是Java对象的一部分,用于存储对象自己的运行时数据,包括以下内容: Mark Word: 用来锁定对象、记录对象哈希值、记录对象所属的分代年龄等信息。 Class: 指向对象的Class对象。 在Java 8中,对象…

    Java 2023年5月26日
    00
  • Eclipse在线安装hibernate插件

    下面是“Eclipse在线安装Hibernate插件”的完整攻略。 安装步骤 打开Eclipse IDE,点击菜单栏上的 Help -> Eclipse Marketplace 进入插件市场。 在搜索框中输入 hibernate,点击搜索按钮,等待搜索结果出现。 选择需要安装的 Hibernate Tools 插件,点击右侧的 Install 按钮,进…

    Java 2023年5月20日
    00
  • Java基础之String类使用与字符串比较

    Java基础之String类使用与字符串比较 String类 在Java中,String类是一个非常常用的类,它代表不可变的Unicode字符序列。任何字符串常量都被看作是String类的实例。例如: String str1 = "Hello"; String str2 = "World"; String str3 =…

    Java 2023年5月26日
    00
合作推广
合作推广
分享本页
返回顶部