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基础-数组扩容详解

    Java基础-数组扩容详解 什么是数组扩容 在Java中,数组是一个固定长度的数据结构。当我们在使用数组时,如果需要添加更多的元素,则需要声明一个新的数组并复制所有旧元素到新数组中。这个过程称为“数组扩容”。 在Java中,数组扩容是自动完成的。当我们向一个已经装满元素的数组中添加新元素时,系统会自动创建一个新的数组,并将旧元素复制到新数组中。这个过程对用户…

    Java 2023年5月26日
    00
  • Java 中解决Unsupported major.minor version 51.0的问题

    当我们编写一个Java程序时,可能会遇到“Unsupported major.minor version 51.0”的错误。这是因为Java程序的class文件有不同的版本,如果运行该程序的Java虚拟机版本比程序编译的版本低,则会出现该错误。以下是解决该问题的完整攻略: 问题分析 我们先来了解一下错误信息的含义。在错误信息中,“major.minor ve…

    Java 2023年5月20日
    00
  • 详解SpringBoot启动代码和自动装配源码分析

    详解 Spring Boot 启动代码和自动装配源码分析 在本文中,我们将详细讲解 Spring Boot 启动代码和自动装配源码分析的完整攻略。我们将使用 Spring Boot 2.5.0 版本的源码进行分析。 步骤一:下载源码 首先,我们需要下载 Spring Boot 2.5.0 版本的源码。可以从官方网站或者 GitHub 上下载。 步骤二:分析启…

    Java 2023年5月15日
    00
  • 什么是G1收集器?

    G1 (Garbage-First)收集器是一款面向服务器端的垃圾收集器,它是JDK 9之后默认的垃圾收集器。与CMS和Parallel Scavenge收集器相比,G1收集器具有更好的吞吐量和更短的暂停时间。接下来,我们将详细讲解G1收集器的使用攻略,包括以下内容: G1收集器的优势和适用场景 G1收集器的参数调优 G1收集器的使用示例 G1收集器的优势和…

    Java 2023年5月10日
    00
  • 超全MyBatis动态代理详解(绝对干货)

    针对“超全MyBatis动态代理详解(绝对干货)”这个主题,我可以提供如下详细讲解。 MyBatis动态代理详解 什么是动态代理? 动态代理是Java中一种常见的设计模式,它通过在程序运行的时候动态创建一个实现某个接口的代理对象,来替代原本需要代码实现的过程。动态代理有着很多优秀的特性,比如代码简洁,易维护等等。 MyBatis动态代理是什么? MyBati…

    Java 2023年5月20日
    00
  • Java HttpClient-Restful工具各种请求高度封装提炼及总结

    Java HttpClient-Restful工具各种请求高度封装提炼及总结 Java中的HttpClient和Restful工具是一些非常实用的工具,可用于完成HTTP请求的各种操作。本文将介绍如何使用Java HttpClient和Restful工具来实现HTTP请求的高度封装,并提供一些示例来帮助读者更好地理解。 HttpClient工具 1.为什么需…

    Java 2023年5月26日
    00
  • springsecurity中http.permitall与web.ignoring的区别说明

    在Spring Security中,我们可以使用http.permitAll()或者web.ignoring()来配置哪些接口需要放行。这两个方法虽然都可以达到相同的效果,但它们的实现方式有所不同。 http.permitAll() 是Spring Security提供的一个方法,它允许我们定义一组匹配URL的表达式,这些URL可以被所有用户访问。例如: p…

    Java 2023年5月20日
    00
  • uniapp 获取系统信息的方法小结

    下面是详细讲解“UniApp 获取系统信息的方法小结”的完整攻略。 简介 UniApp 是一款跨平台开发框架,可支持将一份代码编译成多个平台的应用程序。在 UniApp 应用程序中,我们通常需要获取设备的一些系统信息,比如设备型号、操作系统版本等。UniApp 提供了几个 API 可以帮助我们获取这些系统信息。本文将对这些 API 进行总结和讲解。 获取设备…

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