阅读视图

发现新文章,点击刷新页面。

将找到的值乘以 2

方法一:排序

思路与算法

如果我们不对数组 $\textit{nums}$ 进行任何操作,那么每次更新 $\textit{original}$ 后,都需要 $O(n)$ 的时间完整遍历一遍。最终时间复杂度为 $O(n^2)$。

我们可以对这一过程进行优化。具体而言,每次在数组中找到 $\textit{original}$ 后,$\textit{original}$ 的数值都会比更新前更大,因此我们可以先将数组 $\textit{nums}$ 升序排序,这样每次更新后的 $\textit{original}$ 数值在数组中的位置(如有)只可能位于更新前的后面,我们只需要一边从左至右遍历排序后的 $\textit{nums}$ 数组一边尝试更新 $\textit{original}$ 即可。

代码

###C++

class Solution {
public:
    int findFinalValue(vector<int>& nums, int original) {
        sort(nums.begin(), nums.end());
        for (int num: nums) {
            if (original == num) {
                original *= 2;
            }
        }
        return original;
    }
};

###Python

class Solution:
    def findFinalValue(self, nums: List[int], original: int) -> int:
        nums.sort()
        for num in nums:
            if num == original:
                original *= 2
        return original

###Java

class Solution {
    public int findFinalValue(int[] nums, int original) {
        Arrays.sort(nums);
        for (int num : nums) {
            if (original == num) {
                original *= 2;
            }
        }
        return original;
    }
}

###C#

public class Solution {
    public int FindFinalValue(int[] nums, int original) {
        Array.Sort(nums);
        foreach (int num in nums) {
            if (original == num) {
                original *= 2;
            }
        }
        return original;
    }
}

###Go

func findFinalValue(nums []int, original int) int {
    sort.Ints(nums)
    for _, num := range nums {
        if original == num {
            original *= 2
        }
    }
    return original
}

###C

int cmp(const void* a, const void* b) {
    return (*(int*)a - *(int*)b);
}

int findFinalValue(int* nums, int numsSize, int original) {
    qsort(nums, numsSize, sizeof(int), cmp);
    for (int i = 0; i < numsSize; i++) {
        if (original == nums[i]) {
            original *= 2;
        }
    }
    return original;
}

###JavaScript

var findFinalValue = function(nums, original) {
    nums.sort((a, b) => a - b);
    for (const num of nums) {
        if (original === num) {
            original *= 2;
        }
    }
    return original;
};

###TypeScript

function findFinalValue(nums: number[], original: number): number {
    nums.sort((a, b) => a - b);
    for (const num of nums) {
        if (original === num) {
            original *= 2;
        }
    }
    return original;
}

###Rust

impl Solution {
    pub fn find_final_value(nums: Vec<i32>, original: i32) -> i32 {
        let mut nums = nums;
        nums.sort();
        let mut original = original;
        for &num in nums.iter() {
            if original == num {
                original *= 2;
            }
        }
        original
    }
}

复杂度分析

  • 时间复杂度:$O(n \log n)$,其中 $n$ 为 $\textit{nums}$ 的长度。排序的时间复杂度为 $O(n \log n)$,遍历更新 $\textit{original}$ 的时间复杂度最多为 $O(n)$。

  • 空间复杂度:$O(\log n)$,即为排序的栈空间开销。

方法二:哈希表

思路与算法

我们还可以采用更加直接地利用空间换取时间的方法:利用哈希集合存储数组 $\textit{nums}$ 中的元素,然后我们只需要每次判断 $\textit{original}$ 是否位于该哈希集合中即可。具体地:

  • 如果 $\textit{original}$ 位于哈希集合中,我们将 $\textit{original}$ 乘以 $2$,然后再次判断;

  • 如果 $\textit{original}$ 不位于哈希集合中,那么循环结束,我们返回当前的 $\textit{original}$ 作为答案。

代码

###C++

class Solution {
public:
    int findFinalValue(vector<int>& nums, int original) {
        unordered_set<int> s(nums.begin(), nums.end());
        while (s.count(original)) {
            original *= 2;
        }
        return original;
    }
};

###Python

class Solution:
    def findFinalValue(self, nums: List[int], original: int) -> int:
        s = set(nums)
        while original in s:
            original *= 2
        return original

###Java

class Solution {
    public int findFinalValue(int[] nums, int original) {
        Set<Integer> set = new HashSet<>();
        for (int num : nums) {
            set.add(num);
        }
        while (set.contains(original)) {
            original *= 2;
        }
        return original;
    }
}

###C#

public class Solution {
    public int FindFinalValue(int[] nums, int original) {
        HashSet<int> set = new HashSet<int>(nums);
        while (set.Contains(original)) {
            original *= 2;
        }
        return original;
    }
}

###Go

func findFinalValue(nums []int, original int) int {
    set := make(map[int]bool)
    for _, num := range nums {
        set[num] = true
    }
    for set[original] {
        original *= 2
    }
    return original
}

###C

typedef struct {
    int key;
    UT_hash_handle hh;
} HashItem; 

HashItem *hashFindItem(HashItem **obj, int key) {
    HashItem *pEntry = NULL;
    HASH_FIND_INT(*obj, &key, pEntry);
    return pEntry;
}

bool hashAddItem(HashItem **obj, int key) {
    if (hashFindItem(obj, key)) {
        return false;
    }
    HashItem *pEntry = (HashItem *)malloc(sizeof(HashItem));
    pEntry->key = key;
    HASH_ADD_INT(*obj, key, pEntry);
    return true;
}

void hashFree(HashItem **obj) {
    HashItem *curr = NULL, *tmp = NULL;
    HASH_ITER(hh, *obj, curr, tmp) {
        HASH_DEL(*obj, curr);  
        free(curr);
    }
}

int findFinalValue(int* nums, int numsSize, int original) {
    HashItem *set = NULL;
    for (int i = 0; i < numsSize; i++) {
        hashAddItem(&set, nums[i]);
    }
    while (hashFindItem(&set, original)) {
        original *= 2;
    }
    hashFree(&set);
    return original;
}

###JavaScript

var findFinalValue = function(nums, original) {
    const set = new Set(nums);
    while (set.has(original)) {
        original *= 2;
    }
    return original;
};

###TypeScript

function findFinalValue(nums: number[], original: number): number {
    const set = new Set(nums);
    while (set.has(original)) {
        original *= 2;
    }
    return original;
}

###Rust

use std::collections::HashSet;

impl Solution {
    pub fn find_final_value(nums: Vec<i32>, original: i32) -> i32 {
        let set: HashSet<_> = nums.into_iter().collect();
        let mut original = original;
        while set.contains(&original) {
            original *= 2;
        }
        original
    }
}

复杂度分析

  • 时间复杂度:$O(n)$,其中 $n$ 为 $\textit{nums}$ 的长度。遍历数组维护元素哈希集合的时间复杂度为 $O(n)$,遍历更新 $\textit{original}$ 的时间复杂度最多为 $O(n)$。

  • 空间复杂度:$O(n)$,即为元素哈希集合的空间开销。

[Python3/Java/C++/Go/TypeScript] 一题一解:哈希表(清晰题解)

方法一:哈希表

我们用一个哈希表 $\textit{s}$ 记录数组 $\textit{nums}$ 中的所有数字。

接下来,我们从 $\textit{original}$ 开始,如果 $\textit{original}$ 在 $\textit{s}$ 中,我们将 $\textit{original}$ 乘以 $2$,直到 $\textit{original}$ 不在 $\textit{s}$ 中,返回 $\textit{original}$。

###python

class Solution:
    def findFinalValue(self, nums: List[int], original: int) -> int:
        s = set(nums)
        while original in s:
            original <<= 1
        return original

###java

class Solution {

    public int findFinalValue(int[] nums, int original) {
        Set<Integer> s = new HashSet<>();
        for (int num : nums) {
            s.add(num);
        }
        while (s.contains(original)) {
            original <<= 1;
        }
        return original;
    }
}

###cpp

class Solution {
public:
    int findFinalValue(vector<int>& nums, int original) {
        unordered_set<int> s(nums.begin(), nums.end());
        while (s.contains(original)) {
            original <<= 1;
        }
        return original;
    }
};

###go

func findFinalValue(nums []int, original int) int {
s := map[int]bool{}
for _, x := range nums {
s[x] = true
}
for s[original] {
original <<= 1
}
return original
}

###ts

function findFinalValue(nums: number[], original: number): number {
    const s: Set<number> = new Set([...nums]);
    while (s.has(original)) {
        original <<= 1;
    }
    return original;
}

###rust

impl Solution {
    pub fn find_final_value(nums: Vec<i32>, original: i32) -> i32 {
        use std::collections::HashSet;
        let s: HashSet<i32> = nums.into_iter().collect();
        let mut original = original;
        while s.contains(&original) {
            original <<= 1;
        }
        original
    }
}

时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 为数组 $\textit{nums}$ 的长度。


有任何问题,欢迎评论区交流,欢迎评论区提供其它解题思路(代码),也可以点个赞支持一下作者哈 😄~

每日一题-将找到的值乘以 2🟢

给你一个整数数组 nums ,另给你一个整数 original ,这是需要在 nums 中搜索的第一个数字。

接下来,你需要按下述步骤操作:

  1. 如果在 nums 中找到 original ,将 original 乘以 2 ,得到新 original(即,令 original = 2 * original)。
  2. 否则,停止这一过程。
  3. 只要能在数组中找到新 original ,就对新 original 继续 重复 这一过程

返回 original最终 值。

 

示例 1:

输入:nums = [5,3,6,1,12], original = 3
输出:24
解释: 
- 3 能在 nums 中找到。3 * 2 = 6 。
- 6 能在 nums 中找到。6 * 2 = 12 。
- 12 能在 nums 中找到。12 * 2 = 24 。
- 24 不能在 nums 中找到。因此,返回 24 。

示例 2:

输入:nums = [2,7,9], original = 4
输出:4
解释:
- 4 不能在 nums 中找到。因此,返回 4 。

 

提示:

  • 1 <= nums.length <= 1000
  • 1 <= nums[i], original <= 1000

【track & traning】思路简单,性能高效接近100

方便快速学习算法与理解~

🌇 点赞 👍 收藏 ⭐留言 📝 一键三连 ~关注Jam,从你我做起!

兄弟会背叛你,女人会离开你,金钱会诱惑你,生活会刁难你,只有数学不会,不会就是不会
天才与否,取决于最终达到的高度。真正的天才是那些脚踏实地的人
静下心来好好做自己,走稳脚下每一步,就是最好的路,强者都是孤独的

推荐 python 算法的书籍,体系化学习算法与数据结构,用正确的方式成为offer收割机
leetcode —— 系统化快速学习算法,这不是内卷,这只是悄悄地努力,然后惊艳所有的人
image.png


求解思路

暴力破解

代码

###python3

class Solution:
    def findFinalValue(self, nums: List[int], original: int) -> int:
        for _ in range(len(nums)+1):
            if original in set(nums):
                original *= 2
            else:
                return original

巧妙利用位运算优化空间复杂度:从 O(n) 到 O(log U) 到 O(1)

题意

返回不在 $\textit{nums}$ 中的最小的 $\textit{original}\cdot 2^k$,其中 $k$ 是非负整数。

方法一:枚举

枚举 $k=0,1,2,\ldots$ 判断 $\textit{original}\cdot 2^k$ 是否在 $\textit{nums}$ 中。

用哈希集合记录 $\textit{nums}$ 的每个元素可以加快判断。

class Solution:
    def findFinalValue(self, nums: List[int], original: int) -> int:
        st = set(nums)
        while original in st:
            original *= 2
        return original
class Solution {
    public int findFinalValue(int[] nums, int original) {
        Set<Integer> st = new HashSet<>();
        for (int x : nums) {
            st.add(x);
        }

        while (st.contains(original)) {
            original *= 2;
        }
        return original;
    }
}
class Solution {
public:
    int findFinalValue(vector<int>& nums, int original) {
        unordered_set<int> st(nums.begin(), nums.end());
        while (st.contains(original)) {
            original *= 2;
        }
        return original;
    }
};
func findFinalValue(nums []int, original int) int {
has := map[int]bool{}
for _, x := range nums {
has[x] = true
}

for has[original] {
original *= 2
}
return original
}
var findFinalValue = function(nums, original) {
    const st = new Set(nums);
    while (st.has(original)) {
        original *= 2;
    }
    return original;
};
use std::collections::HashSet;

impl Solution {
    pub fn find_final_value(nums: Vec<i32>, mut original: i32) -> i32 {
        let st = nums.into_iter().collect::<HashSet<_>>();
        while st.contains(&original) {
            original *= 2;
        }
        original
    }
}

复杂度分析

  • 时间复杂度:$\mathcal{O}\left(n+\log\dfrac{U}{\textit{original}}\right)$,其中 $n$ 是 $\textit{nums}$ 的长度,$U=\max(\textit{nums})$。循环次数为 $\mathcal{O}\left(\log\dfrac{U}{\textit{original}}\right)$。
  • 空间复杂度:$\mathcal{O}(n)$。

方法二:只记录所有可能值

哈希集合记录的元素可以更少,只需要记录符合 $\textit{original}\cdot 2^k$ 的元素。

设 $x = \textit{nums}[i]$,如果 $x = \textit{original}\cdot 2^k$,那么:

  • $x$ 是 $\textit{original}$ 的倍数。
  • $\dfrac{x}{\textit{original}}$ 是 2 的幂,见 我的题解
class Solution:
    def findFinalValue(self, nums: List[int], original: int) -> int:
        st = set()
        for x in nums:
            k, r = divmod(x, original)
            if r == 0 and k & (k - 1) == 0:
                st.add(x)

        while original in st:
            original *= 2
        return original
class Solution {
    public int findFinalValue(int[] nums, int original) {
        Set<Integer> st = new HashSet<>();
        for (int x : nums) {
            int k = x / original;
            if (x % original == 0 && (k & (k - 1)) == 0) {
                st.add(x);
            }
        }

        while (st.contains(original)) {
            original *= 2;
        }
        return original;
    }
}
class Solution {
public:
    int findFinalValue(vector<int>& nums, int original) {
        unordered_set<int> st;
        for (int x : nums) {
            int k = x / original;
            if (x % original == 0 && (k & (k - 1)) == 0) {
                st.insert(x);
            }
        }

        while (st.contains(original)) {
            original *= 2;
        }
        return original;
    }
};
func findFinalValue(nums []int, original int) int {
has := map[int]bool{}
for _, x := range nums {
k := x / original
if x%original == 0 && k&(k-1) == 0 {
has[x] = true
}
}
for has[original] {
original *= 2
}
return original
}
var findFinalValue = function(nums, original) {
    const st = new Set();
    for (const x of nums) {
        const k = x / original;
        if (x % original === 0 && (k & (k - 1)) === 0) {
            st.add(x);
        }
    }

    while (st.has(original)) {
        original *= 2;
    }
    return original;
};
use std::collections::HashSet;

impl Solution {
    pub fn find_final_value(nums: Vec<i32>, mut original: i32) -> i32 {
        let st = nums.into_iter()
            .filter(|x| x % original == 0 && ((x / original) & (x / original - 1)) == 0)
            .collect::<HashSet<_>>();
        while st.contains(&original) {
            original *= 2;
        }
        original
    }
}

复杂度分析

  • 时间复杂度:$\mathcal{O}\left(n+\log\dfrac{U}{\textit{original}}\right)$,其中 $n$ 是 $\textit{nums}$ 的长度,$U=\max(\textit{nums})$。
  • 空间复杂度:$\mathcal{O}\left(\log\dfrac{U}{\textit{original}}\right)$。

方法三:位运算

改成记录 $\textit{original}\cdot 2^k$ 中的 $k$。

由于 $k$ 很小,我们可以把 $k$ 保存到一个二进制数 $\textit{mask}$ 中,具体请看 从集合论到位运算,常见位运算技巧分类总结!

答案中的 $k$ 就是 $\textit{mask}$ 的最低 $0$ 的位置。

这可以通过位运算 $\mathcal{O}(1)$ 地计算出来:把 $\textit{mask}$ 取反,找最低位的 $1$,其对应的二进制数 $\textit{lowbit}$ 即为答案中的 $2^k$。再乘以 $\textit{original}$,得到最终答案。

:找最低 $0$ 对应的 $2^k$,也可以直接计算 (mask + 1) & ~mask

class Solution:
    def findFinalValue(self, nums: List[int], original: int) -> int:
        mask = 0
        for x in nums:
            k, r = divmod(x, original)
            if r == 0 and k & (k - 1) == 0:
                mask |= k

        # 找最低的 0,等价于取反后,找最低的 1(lowbit)
        mask = ~mask
        return original * (mask & -mask)
class Solution {
    public int findFinalValue(int[] nums, int original) {
        int mask = 0;
        for (int x : nums) {
            int k = x / original;
            if (x % original == 0 && (k & (k - 1)) == 0) {
                mask |= k;
            }
        }

        // 找最低的 0,等价于取反后,找最低的 1(lowbit)
        mask = ~mask;
        return original * (mask & -mask);
    }
}
class Solution {
public:
    int findFinalValue(vector<int>& nums, int original) {
        int mask = 0;
        for (int x : nums) {
            int k = x / original;
            if (x % original == 0 && (k & (k - 1)) == 0) {
                mask |= k;
            }
        }

        // 找最低的 0,等价于取反后,找最低的 1(lowbit)
        mask = ~mask;
        return original * (mask & -mask);
    }
};
func findFinalValue(nums []int, original int) int {
mask := 0
for _, x := range nums {
k := x / original
if x%original == 0 && k&(k-1) == 0 {
mask |= k
}
}

// 找最低的 0,等价于取反后,找最低的 1(lowbit)
mask = ^mask
return original * (mask & -mask)
}
var findFinalValue = function(nums, original) {
    let mask = 0;
    for (const x of nums) {
        const k = x / original;
        if (x % original === 0 && (k & (k - 1)) === 0) {
            mask |= k;
        }
    }

    // 找最低的 0,等价于取反后,找最低的 1(lowbit)
    mask = ~mask;
    return original * (mask & -mask);
};
impl Solution {
    pub fn find_final_value(nums: Vec<i32>, original: i32) -> i32 {
        let mut mask = 0;
        for x in nums {
            let k = x / original;
            if x % original == 0 && (k & (k - 1)) == 0 {
                mask |= k;
            }
        }

        // 找最低的 0,等价于取反后,找最低的 1(lowbit)
        mask = !mask;
        original * (mask & -mask)
    }
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(n)$,其中 $n$ 是 $\textit{nums}$ 的长度。
  • 空间复杂度:$\mathcal{O}(1)$。

相似题目

2568. 最小无法得到的或值

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

题意解读,简洁写法(Python/Java/C++/C/Go/JS/Rust)

题意

有一个包含 $\texttt{a}$ 和 $\texttt{b}$ 的字符串 $s$,把其中的 $\texttt{a}$ 替换成 $0$,$\texttt{b}$ 替换成 $1,0$,得到一个 $01$ 序列 $\textit{bits}$。

你需要把 $\textit{bits}$ 还原回字符串 $s$,判断 $s$ 的最后一个字符是不是 $\texttt{a}$。

思路

根据题意,两种字符替换后的第一个数字一定是不同的,一个是 $0$,另一个是 $1$。

换句话说,我们可以通过 $\textit{bits}[0]$ 确定字符串的第一个字符:

  • 如果 $\textit{bits}[0]=0$,那么是 $\texttt{a}$,把 $\textit{bits}[0]$ 去掉。
  • 如果 $\textit{bits}[0]=1$,那么是 $\texttt{b}$,把 $\textit{bits}[0]$ 和 $\textit{bits}[1]$ 去掉。

重复该过程,直到 $\textit{bits}$ 剩下至多一个数字。

分类讨论:

  • 如果最后剩下一个数字,由于题目保证 $\textit{bits}[n-1] = 0$,所以字符串的最后一个字符是 $\texttt{a}$,返回 $\texttt{true}$。
  • 如果最后没有剩下数字,说明字符串的最后一个字符是 $\texttt{b}$,返回 $\texttt{false}$。

示例 1 是 $[1,0] + [0]$,满足要求。

示例 2 是 $[1,1] + [1,0]$,不满足要求。

class Solution:
    def isOneBitCharacter(self, bits: List[int]) -> bool:
        n = len(bits)
        i = 0
        while i < n - 1:  # 循环直到剩下至多一个数字
            i += bits[i] + 1  # 如果 bits[i] == 1 则跳过下一位
        return i == n - 1  # 注意题目保证 bits[n-1] == 0,无需判断
class Solution {
    public boolean isOneBitCharacter(int[] bits) {
        int n = bits.length;
        int i = 0;
        while (i < n - 1) { // 循环直到剩下至多一个数字
            i += bits[i] + 1; // 如果 bits[i] == 1 则跳过下一位
        }
        return i == n - 1; // 注意题目保证 bits[n-1] == 0,无需判断
    }
}
class Solution {
public:
    bool isOneBitCharacter(vector<int>& bits) {
        int n = bits.size();
        int i = 0;
        while (i < n - 1) { // 循环直到剩下至多一个数字
            i += bits[i] + 1; // 如果 bits[i] == 1 则跳过下一位
        }
        return i == n - 1; // 注意题目保证 bits[n-1] == 0,无需判断
    }
};
bool isOneBitCharacter(int* bits, int bitsSize) {
    int i = 0;
    while (i < bitsSize - 1) { // 循环直到剩下至多一个数字
        i += bits[i] + 1; // 如果 bits[i] == 1 则跳过下一位
    }
    return i == bitsSize - 1; // 注意题目保证 bits[n-1] == 0,无需判断
}
func isOneBitCharacter(bits []int) bool {
n := len(bits)
i := 0
for i < n-1 { // 循环直到剩下至多一个数字
i += bits[i] + 1 // 如果 bits[i] == 1 则跳过下一位
}
return i == n-1 // 注意题目保证 bits[n-1] == 0,无需判断
}
var isOneBitCharacter = function(bits) {
    const n = bits.length;
    let i = 0;
    while (i < n - 1) { // 循环直到剩下至多一个数字
        i += bits[i] + 1; // 如果 bits[i] == 1 则跳过下一位
    }
    return i === n - 1; // 注意题目保证 bits[n-1] == 0,无需判断
};
impl Solution {
    pub fn is_one_bit_character(bits: Vec<i32>) -> bool {
        let n = bits.len();
        let mut i = 0;
        while i < n - 1 { // 循环直到剩下至多一个数字
            i += (bits[i] + 1) as usize; // 如果 bits[i] == 1 则跳过下一位
        }
        i == n - 1 // 注意题目保证 bits[n-1] == 0,无需判断
    }
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(n)$,其中 $n$ 是 $\textit{bits}$ 的长度。
  • 空间复杂度:$\mathcal{O}(1)$。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

每日一题-1 比特与 2 比特字符🟢

有两种特殊字符:

  • 第一种字符可以用一比特 0 表示
  • 第二种字符可以用两比特(10 或 11)表示

给你一个以 0 结尾的二进制数组 bits ,如果最后一个字符必须是一个一比特字符,则返回 true

 

示例 1:

输入: bits = [1, 0, 0]
输出: true
解释: 唯一的解码方式是将其解析为一个两比特字符和一个一比特字符。
所以最后一个字符是一比特字符。

示例 2:

输入:bits = [1,1,1,0]
输出:false
解释:唯一的解码方式是将其解析为两比特字符和两比特字符。
所以最后一个字符不是一比特字符。

 

提示:

  • 1 <= bits.length <= 1000
  • bits[i]01

【负雪明烛】图解算法:走一步 or 走两步

大家好,我是 @负雪明烛。点击右上方的「+关注,优质题解不间断!

题目大意

有两种字符,一种是 0,一种是10或者11

给出了一个由这两种字符组成的数组,判断最后一位的数字是否一定是单个的 0

解题方法

遍历

有两种字符串,一种是 0,一种是 1011。即一种长度是1,一种长度是2.

所以找个指针然后遍历一遍:

  • 遇到 0 走一步;
  • 遇到 1走两步。

题目告诉了数组的最后一个元素一定是 0,所以最后如果恰好到达len-1,说明最后一个数字的长度为 1 ,也就是 0,就满足题意了。

717. 1比特与2比特字符.001.png

代码如下:

###Java

class Solution {
    public boolean isOneBitCharacter(int[] bits) {
        int N = bits.length;
        int pos = 0;
        while (pos < N - 1) {
            pos += bits[pos] == 1 ? 2 : 1;
        }
        return pos == N - 1;
    }
}

###Python

class Solution(object):
    def isOneBitCharacter(self, bits):
        """
        :type bits: List[int]
        :rtype: bool
        """
        pos = 0
        while pos < len(bits) - 1:
            pos += 2 if bits[pos] == 1 else 1
        return pos == len(bits) - 1

###C++

class Solution {
public:
    bool isOneBitCharacter(vector<int>& bits) {
        const int N = bits.size();
        int pos = 0;
        while (pos < N - 1) {
            pos += bits[pos] == 1 ? 2 : 1;
        }
        return pos == N - 1;
    }
};

时间复杂度

  • 时间复杂度:$O(N)$,其中 $N$ 是数组长度;
  • 空间复杂度:$O(1)$。

总结

  1. 单指针根据题目要求进行移动,最重要的还是理解题意哇!!

image.png


我是 @负雪明烛 ,刷算法题 1000 多道,写了 1000 多篇算法题解,收获阅读量 300 万。
关注我,你将不会错过我的精彩动画题解、面试题分享、组队刷题活动,进入主页 @负雪明烛 右侧有刷题组织,从此刷题不再孤单。

【宫水三叶】简单模拟题

模拟

根据题意进行模拟即可,使用 $n$ 代指 bits 的长度,$idx$ 为当前「比特字」的开头,从前往后扫描每个「比特字」,如果最后一个「比特字」的开头为 $n - 1$ 返回 True,否则返回 False

代码:

###Java

class Solution {
    public boolean isOneBitCharacter(int[] bits) {
        int n = bits.length, idx = 0;
        while (idx < n - 1) {
            if (bits[idx] == 0) idx++;
            else idx += 2;
        }
        return idx == n - 1;
    }
}
  • 时间复杂度:$O(n)$
  • 空间复杂度:$O(1)$

加练「滑动窗口/模拟」内容

题太简单?不如来学习热乎的 一道结合众多知识点的滑窗综合题 🎉🎉

考虑加练如下「模拟」相关内容 🍭🍭🍭

题目 题解 难度 推荐指数
2. 两数相加 LeetCode 题解链接 中等 🤩🤩🤩🤩🤩
5. 最长回文子串 LeetCode 题解链接 中等 🤩🤩🤩🤩🤩
6. Z 字形变换 LeetCode 题解链接 中等 🤩🤩🤩
8. 字符串转换整数 (atoi) LeetCode 题解链接 中等 🤩🤩🤩
12. 整数转罗马数字 LeetCode 题解链接 中等 🤩🤩
31. 下一个排列 LeetCode 题解链接 中等 🤩🤩🤩
43. 字符串相乘 LeetCode 题解链接 中等 🤩🤩🤩🤩
58. 最后一个单词的长度 LeetCode 题解链接 中等 🤩🤩🤩🤩
59. 螺旋矩阵 II LeetCode 题解链接 中等 🤩🤩🤩🤩
68. 文本左右对齐 LeetCode 题解链接 困难 🤩🤩🤩
71. 简化路径 LeetCode 题解链接 中等 🤩🤩🤩🤩
73. 矩阵置零 LeetCode 题解链接 中等 🤩🤩🤩🤩
89. 格雷编码 LeetCode 题解链接 中等 🤩🤩🤩🤩
165. 比较版本号 LeetCode 题解链接 中等 🤩🤩🤩🤩
166. 分数到小数 LeetCode 题解链接 中等 🤩🤩🤩🤩
233. 数字 1 的个数 LeetCode 题解链接 困难 🤩🤩🤩🤩
260. 只出现一次的数字 III LeetCode 题解链接 中等 🤩🤩🤩🤩
273. 整数转换英文表示 LeetCode 题解链接 困难 🤩🤩🤩🤩
284. 顶端迭代器 LeetCode 题解链接 中等 🤩🤩🤩🤩
299. 猜数字游戏 LeetCode 题解链接 中等 🤩🤩🤩🤩
335. 路径交叉 LeetCode 题解链接 困难 🤩🤩🤩🤩
382. 链表随机节点 LeetCode 题解链接 中等 🤩🤩🤩🤩🤩
400. 第 N 位数字 LeetCode 题解链接 中等 🤩🤩🤩🤩
413. 等差数列划分 LeetCode 题解链接 中等 🤩🤩🤩🤩
414. 第三大的数 LeetCode 题解链接 中等 🤩🤩🤩🤩
419. 甲板上的战舰 LeetCode 题解链接 中等 🤩🤩🤩🤩
423. 从英文中重建数字 LeetCode 题解链接 中等 🤩🤩🤩🤩
443. 压缩字符串 LeetCode 题解链接 中等 🤩🤩🤩🤩
451. 根据字符出现频率排序 LeetCode 题解链接 中等 🤩🤩🤩🤩
457. 环形数组是否存在循环 LeetCode 题解链接 中等 🤩🤩🤩🤩
528. 按权重随机选择 LeetCode 题解链接 中等 🤩🤩🤩🤩
539. 最小时间差 LeetCode 题解链接 中等 🤩🤩🤩🤩
726. 原子的数量 LeetCode 题解链接 困难 🤩🤩🤩🤩
794. 有效的井字游戏 LeetCode 题解链接 中等 🤩🤩🤩🤩
846. 一手顺子 LeetCode 题解链接 中等 🤩🤩🤩
859. 亲密字符串 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
1001. 网格照明 LeetCode 题解链接 困难 🤩🤩🤩🤩
1005. K 次取反后最大化的数组和 LeetCode 题解链接 简单 🤩🤩🤩🤩
1436. 旅行终点站 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
1446. 连续字符 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
1480. 一维数组的动态和 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
1583. 统计不开心的朋友 LeetCode 题解链接 中等 🤩🤩🤩🤩
1614. 括号的最大嵌套深度 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
1743. 从相邻元素对还原数组 LeetCode 题解链接 中等 🤩🤩🤩🤩
1834. 单线程 CPU LeetCode 题解链接 中等 🤩🤩🤩🤩
1894. 找到需要补充粉笔的学生编号 LeetCode 题解链接 中等 🤩🤩🤩🤩
面试题 10.02. 变位词组 LeetCode 题解链接 中等 🤩🤩🤩🤩

注:以上目录整理来自 wiki,任何形式的转载引用请保留出处。


最后

如果有帮助到你,请给题解点个赞和收藏,让更多的人看到 ~ ("▔□▔)/

也欢迎你 关注我 和 加入我们的「组队打卡」小群 ,提供写「证明」&「思路」的高质量题解。

所有题解已经加入 刷题指南,欢迎 star 哦 ~

[Python3/Java/C++/Go/TypeScript] 一题一解:模拟(清晰题解)

方法一:模拟

我们可以遍历数组 $\textit{nums}$,用变量 $j$ 记录上一个 $1$ 的下标,那么当前位置 $i$ 的元素为 $1$ 时,只需要判断 $i - j - 1$ 是否小于 $k$ 即可。如果小于 $k$,则说明存在两个 $1$ 之间的 $0$ 的个数小于 $k$,返回 $\text{false}$;否则,将 $j$ 更新为 $i$,继续遍历数组。

遍历结束后,返回 $\text{true}$。

###python

class Solution:
    def kLengthApart(self, nums: List[int], k: int) -> bool:
        j = -inf
        for i, x in enumerate(nums):
            if x:
                if i - j - 1 < k:
                    return False
                j = i
        return True

###java

class Solution {
    public boolean kLengthApart(int[] nums, int k) {
        int j = -(k + 1);
        for (int i = 0; i < nums.length; ++i) {
            if (nums[i] == 1) {
                if (i - j - 1 < k) {
                    return false;
                }
                j = i;
            }
        }
        return true;
    }
}

###cpp

class Solution {
public:
    bool kLengthApart(vector<int>& nums, int k) {
        int j = -(k + 1);
        for (int i = 0; i < nums.size(); ++i) {
            if (nums[i] == 1) {
                if (i - j - 1 < k) {
                    return false;
                }
                j = i;
            }
        }
        return true;
    }
};

###go

func kLengthApart(nums []int, k int) bool {
j := -(k + 1)
for i, x := range nums {
if x == 1 {
if i-j-1 < k {
return false
}
j = i
}
}
return true
}

###ts

function kLengthApart(nums: number[], k: number): boolean {
    let j = -(k + 1);
    for (let i = 0; i < nums.length; ++i) {
        if (nums[i] === 1) {
            if (i - j - 1 < k) {
                return false;
            }
            j = i;
        }
    }
    return true;
}

###rust

impl Solution {
    pub fn k_length_apart(nums: Vec<i32>, k: i32) -> bool {
        let mut j = -(k + 1);
        for (i, &x) in nums.iter().enumerate() {
            if x == 1 {
                if (i as i32) - j - 1 < k {
                    return false;
                }
                j = i as i32;
            }
        }
        true
    }
}

时间复杂度 $O(n)$,其中 $n$ 为数组 $\textit{nums}$ 的长度。空间复杂度 $O(1)$。


有任何问题,欢迎评论区交流,欢迎评论区提供其它解题思路(代码),也可以点个赞支持一下作者哈 😄~

每日一题-是否所有 1 都至少相隔 k 个元素🟢

给你一个由若干 01 组成的数组 nums 以及整数 k。如果所有 1 都至少相隔 k 个元素,则返回 true ;否则,返回 false

 

示例 1:

输入:nums = [1,0,0,0,1,0,0,1], k = 2
输出:true
解释:每个 1 都至少相隔 2 个元素。

示例 2:

输入:nums = [1,0,0,1,0,1], k = 2
输出:false
解释:第二个 1 和第三个 1 之间只隔了 1 个元素。

 

提示:

  • 1 <= nums.length <= 105
  • 0 <= k <= nums.length
  • nums[i] 的值为 01

简单题,简单做(Python/Java/C++/C/Go/JS/Rust)

如果所有相邻的 $1$ 都至少相隔 $k$ 个元素,那么所有 $1$ 都至少相隔 $k$ 个元素。

所以只需检查相邻的 $1$。

记录上一个 $1$ 的位置 $\textit{last}_1$。

如果 $\textit{nums}[i] = 1$ 且 $i - \textit{last}_1 - 1 < k$,即 $i - \textit{last}_1 \le k$,则不满足要求。

为了兼容 $\textit{nums}$ 的第一个 $1$,可以初始化 $\textit{last}_1 = -k-1$。

###py

class Solution:
    def kLengthApart(self, nums: List[int], k: int) -> bool:
        last1 = -inf
        for i, x in enumerate(nums):
            if x != 1:
                continue
            if i - last1 <= k:
                return False
            last1 = i
        return True

###java

class Solution {
    public boolean kLengthApart(int[] nums, int k) {
        int last1 = -k - 1;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] != 1) {
                continue;
            }
            if (i - last1 <= k) {
                return false;
            }
            last1 = i;
        }
        return true;
    }
}

###cpp

class Solution {
public:
    bool kLengthApart(vector<int>& nums, int k) {
        int last1 = -k - 1;
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] != 1) {
                continue;
            }
            if (i - last1 <= k) {
                return false;
            }
            last1 = i;
        }
        return true;
    }
};

###c

bool kLengthApart(int* nums, int numsSize, int k) {
    int last1 = -k - 1;
    for (int i = 0; i < numsSize; i++) {
        if (nums[i] != 1) {
            continue;
        }
        if (i - last1 <= k) {
            return false;
        }
        last1 = i;
    }
    return true;
}

###go

func kLengthApart(nums []int, k int) bool {
last1 := -k - 1
for i, x := range nums {
if x != 1 {
continue
}
if i-last1 <= k {
return false
}
last1 = i
}
return true
}

###js

var kLengthApart = function(nums, k) {
    let last1 = -Infinity;
    for (let i = 0; i < nums.length; i++) {
        if (nums[i] !== 1) {
            continue;
        }
        if (i - last1 <= k) {
            return false;
        }
        last1 = i;
    }
    return true;
};

###rust

impl Solution {
    pub fn k_length_apart(nums: Vec<i32>, k: i32) -> bool {
        let mut last1 = -k - 1;
        for (i, x) in nums.into_iter().enumerate() {
            if x != 1 {
                continue;
            }
            if i as i32 - last1 <= k {
                return false;
            }
            last1 = i as i32;
        }
        true
    }
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(n)$,其中 $n$ 是 $\textit{nums}$ 的长度。
  • 空间复杂度:$\mathcal{O}(1)$。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

思路简单,性能高效

解题思路

此处撰写解题思路
image.png

代码

###python3

class Solution:
    def kLengthApart(self, nums: List[int], k: int) -> bool:
        if 1 not in nums:
            return True

        min_len = n = len(nums)
        start = nums.index(1)
        for idx in range(start+1,n):
            if nums[idx] == 1:
                min_len = min(min_len, idx - start - 1)
                start = idx
                if min_len < k:
                    return False
        return True



[Python3/Java/C++/Go/TypeScript] 一题一解:遍历计数(清晰题解)

方法一:遍历计数

我们遍历字符串 $s$,用一个变量 $\textit{cur}$ 记录当前连续的 1 的个数,用变量 $\textit{ans}$ 记录答案。当遍历到字符 $s[i]$ 时,如果 $s[i] = 0$,则 $\textit{cur}$ 置 0,否则 $\textit{cur}$ 自增 1,然后 $\textit{ans}$ 自增 $\textit{cur}$,并对 $10^9 + 7$ 取模。

遍历结束,返回 $\textit{ans}$ 即可。

###python

class Solution:
    def numSub(self, s: str) -> int:
        mod = 10**9 + 7
        ans = cur = 0
        for c in s:
            if c == "0":
                cur = 0
            else:
                cur += 1
                ans = (ans + cur) % mod
        return ans

###java

class Solution {
    public int numSub(String s) {
        final int mod = 1_000_000_007;
        int ans = 0, cur = 0;
        for (int i = 0; i < s.length(); ++i) {
            if (s.charAt(i) == '0') {
                cur = 0;
            } else {
                cur++;
                ans = (ans + cur) % mod;
            }
        }
        return ans;
    }
}

###cpp

class Solution {
public:
    int numSub(string s) {
        const int mod = 1e9 + 7;
        int ans = 0, cur = 0;
        for (char c : s) {
            if (c == '0') {
                cur = 0;
            } else {
                cur++;
                ans = (ans + cur) % mod;
            }
        }
        return ans;
    }
};

###go

func numSub(s string) (ans int) {
const mod int = 1e9 + 7
cur := 0
for _, c := range s {
if c == '0' {
cur = 0
} else {
cur++
ans = (ans + cur) % mod
}
}
return
}

###ts

function numSub(s: string): number {
    const mod = 1_000_000_007;
    let [ans, cur] = [0, 0];
    for (const c of s) {
        if (c === '0') {
            cur = 0;
        } else {
            cur++;
            ans = (ans + cur) % mod;
        }
    }
    return ans;
}

###rust

impl Solution {
    pub fn num_sub(s: String) -> i32 {
        const MOD: i32 = 1_000_000_007;
        let mut ans: i32 = 0;
        let mut cur: i32 = 0;
        for c in s.chars() {
            if c == '0' {
                cur = 0;
            } else {
                cur += 1;
                ans = (ans + cur) % MOD;
            }
        }
        ans
    }
}

时间复杂度 $O(n)$,其中 $n$ 为字符串 $s$ 的长度。空间复杂度 $O(1)$。


有任何问题,欢迎评论区交流,欢迎评论区提供其它解题思路(代码),也可以点个赞支持一下作者哈 😄~

每日一题-仅含 1 的子串数🟡

给你一个二进制字符串 s(仅由 '0' 和 '1' 组成的字符串)。

返回所有字符都为 1 的子字符串的数目。

由于答案可能很大,请你将它对 10^9 + 7 取模后返回。

 

示例 1:

输入:s = "0110111"
输出:9
解释:共有 9 个子字符串仅由 '1' 组成
"1" -> 5 次
"11" -> 3 次
"111" -> 1 次

示例 2:

输入:s = "101"
输出:2
解释:子字符串 "1" 在 s 中共出现 2 次

示例 3:

输入:s = "111111"
输出:21
解释:每个子字符串都仅由 '1' 组成

示例 4:

输入:s = "000"
输出:0

 

提示:

  • s[i] == '0's[i] == '1'
  • 1 <= s.length <= 10^5

枚举子串右端点,简洁写法(Python/Java/C++/C/Go/JS/Rust)

遍历 $s$ 的过程中,记录上一个 $0$ 的位置 $\textit{last}_0$。

如果 $s[i]=1$,那么对于右端点为 $i$ 的全 $1$ 子串,左端点可以是

$$
\textit{last}_0+1,\textit{last}_0+2,\ldots,i-1,i
$$

一共有 $i - \textit{last}_0$ 个,加到答案中。

比如 $\textit{last}_0=2$,现在 $s[5]=1$,那么子串 $[3,5],[4,5],[5,5]$ 都只包含 $1$,有 $5-2=3$ 个以 $5$ 为右端点的全 $1$ 子串。

可能一开始没有 $0$,为了让公式兼容这种情况,初始化 $\textit{last}_0=-1$。

注意答案不超过 $64$ 位整数的最大值,可以在最后返回时再取模。

###py

class Solution:
    def numSub(self, s: str) -> int:
        MOD = 1_000_000_007
        ans = 0
        last0 = -1
        for i, ch in enumerate(s):
            if ch == '0':
                last0 = i  # 记录上个 0 的位置
            else:
                ans += i - last0  # 右端点为 i 的全 1 子串个数
        return ans % MOD

###java

class Solution {
    public int numSub(String s) {
        final int MOD = 1_000_000_007;
        long ans = 0;
        int last0 = -1;
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            if (ch == '0') {
                last0 = i; // 记录上个 0 的位置
            } else {
                ans += i - last0; // 右端点为 i 的全 1 子串个数
            }
        }
        return (int) (ans % MOD);
    }
}

###cpp

class Solution {
public:
    int numSub(string s) {
        constexpr int MOD = 1'000'000'007;
        long long ans = 0;
        int last0 = -1;
        for (int i = 0; i < s.size(); i++) {
            if (s[i] == '0') {
                last0 = i; // 记录上个 0 的位置
            } else {
                ans += i - last0; // 右端点为 i 的全 1 子串个数
            }
        }
        return ans % MOD;
    }
};

###c

#define MOD 1000000007

int numSub(char* s) {
    long long ans = 0;
    int last0 = -1;
    for (int i = 0; s[i]; i++) {
        if (s[i] == '0') {
            last0 = i; // 记录上个 0 的位置
        } else {
            ans += i - last0; // 右端点为 i 的全 1 子串个数
        }
    }
    return ans % MOD;
}

###go

func numSub(s string) (ans int) {
const mod = 1_000_000_007
last0 := -1
for i, ch := range s {
if ch == '0' {
last0 = i // 记录上个 0 的位置
} else {
ans += i - last0 // 右端点为 i 的全 1 子串个数
}
}
return ans % mod
}

###js

var numSub = function(s) {
    const MOD = 1_000_000_007;
    let ans = 0, last0 = -1;
    for (let i = 0; i < s.length; i++) {
        if (s[i] === '0') {
            last0 = i; // 记录上个 0 的位置
        } else {
            ans += i - last0; // 右端点为 i 的全 1 子串个数
        }
    }
    return ans % MOD;
};

###rust

impl Solution {
    pub fn num_sub(s: String) -> i32 {
        const MOD: i64 = 1_000_000_007;
        let mut ans = 0;
        let mut last0 = -1;
        for (i, ch) in s.bytes().enumerate() {
            if ch == b'0' {
                last0 = i as i32; // 记录上个 0 的位置
            } else {
                ans += (i as i32 - last0) as i64; // 右端点为 i 的全 1 子串个数
            }
        }
        (ans % MOD) as _
    }
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(n)$,其中 $n$ 是 $s$ 的长度。
  • 空间复杂度:$\mathcal{O}(1)$。

相似题目

2348. 全 0 子数组的数目

专题训练

见下面双指针题单的「六、分组循环」。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

仅含 1 的子串数

方法一:遍历字符串寻找最长子串

如果一个所有字符都为 $1$ 的字符串的长度为 $k$,则该字符串包含的所有字符都为 $1$ 的子字符串(包括该字符串本身)的数量是 $\dfrac{k * (k + 1)}{2}$。

首先寻找到所有的只包含字符 $1$ 的最长子字符串。这里的「只包含字符 $1$ 的最长子字符串」的意思是,假设该子字符串的下标范围是 $[i, j]$(包含下标 $i$ 和下标 $j$),其中 $i \le j$,该子字符串中的所有字符都是 $1$,且下标 $i$ 满足 $i$ 位于字符串 $s$ 的最左侧或者下标 $i - 1$ 位置的字符是 $0$,以及下标 $j$ 满足 $j$ 位于字符串 $s$ 的最右侧或者下标 $j + 1$ 位置的字符是 $0$。

寻找到所有的只包含字符 $1$ 的最长子字符串之后,就可以计算所有字符都为 $1$ 的子字符串的数量。

具体做法是,从左到右遍历字符串,如果遇到字符 $1$ 则计算连续字符 $1$ 的数量,如果遇到字符 $0$ 则说明上一个只包含字符 $1$ 的最长子字符串遍历结束,根据最长子字符串的长度计算子字符串的数量,然后将连续字符 $1$ 的数量清零。遍历结束后,如果连续字符 $1$ 的数量大于零,则还有最后一个只包含字符 $1$ 的最长子字符串,因此还需要计算其对应的子字符串的数量。

###Java

class Solution {
    public int numSub(String s) {
        final int MODULO = 1000000007;
        long total = 0;
        int length = s.length();
        long consecutive = 0;
        for (int i = 0; i < length; i++) {
            char c = s.charAt(i);
            if (c == '0') {
                total += consecutive * (consecutive + 1) / 2;
                total %= MODULO;
                consecutive = 0;
            } else {
                consecutive++;
            }
        }
        total += consecutive * (consecutive + 1) / 2;
        total %= MODULO;
        return (int) total;
    }
}

###C++

class Solution {
public:
    static constexpr int P = int(1E9) + 7;
    
    int numSub(string s) {
        int p = 0;
        long long ans = 0;
        while (p < s.size()) {
            if (s[p] == '0') {
                ++p;
                continue;
            }
            int cnt = 0;
            while (p < s.size() && s[p] == '1') {
                ++cnt;
                ++p;
            }
            ans = ans + (1LL + (long long)cnt) * cnt / 2;
            ans = ans % P;
        }
        return ans;
    }
};

###Python

class Solution:
    def numSub(self, s: str) -> int:
        total, consecutive = 0, 0
        length = len(s)
        for i in range(length):
            if s[i] == '0':
                total += consecutive * (consecutive + 1) // 2
                consecutive = 0
            else:
                consecutive += 1
        
        total += consecutive * (consecutive + 1) // 2
        total %= (10**9 + 7)
        return total

###C#

public class Solution {
    public int NumSub(string s) {
        const int MODULO = 1000000007;
        long total = 0;
        long consecutive = 0;
        foreach (char c in s) {
            if (c == '0') {
                total += consecutive * (consecutive + 1) / 2;
                total %= MODULO;
                consecutive = 0;
            } else {
                consecutive++;
            }
        }
        total += consecutive * (consecutive + 1) / 2;
        total %= MODULO;
        return (int)total;
    }
}

###Go

func numSub(s string) int {
    const MODULO = 1000000007
    total := 0
    consecutive := 0
    for _, c := range s {
        if c == '0' {
            total += consecutive * (consecutive + 1) / 2
            total %= MODULO
            consecutive = 0
        } else {
            consecutive++
        }
    }
    total += consecutive * (consecutive + 1) / 2
    total %= MODULO
    return total
}

###C

int numSub(char * s) {
    const int MODULO = 1000000007;
    long total = 0;
    long consecutive = 0;
    for (int i = 0; s[i]; i++) {
        if (s[i] == '0') {
            total += consecutive * (consecutive + 1) / 2;
            total %= MODULO;
            consecutive = 0;
        } else {
            consecutive++;
        }
    }
    total += consecutive * (consecutive + 1) / 2;
    total %= MODULO;
    return (int)total;
}

###JavaScript

var numSub = function(s) {
    const MODULO = 1000000007;
    let total = 0;
    let consecutive = 0;
    for (const c of s) {
        if (c === '0') {
            total += consecutive * (consecutive + 1) / 2;
            total %= MODULO;
            consecutive = 0;
        } else {
            consecutive++;
        }
    }
    total += consecutive * (consecutive + 1) / 2;
    total %= MODULO;
    return total;
};

###TypeScript

function numSub(s: string): number {
    const MODULO = 1000000007;
    let total = 0;
    let consecutive = 0;
    for (const c of s) {
        if (c === '0') {
            total += consecutive * (consecutive + 1) / 2;
            total %= MODULO;
            consecutive = 0;
        } else {
            consecutive++;
        }
    }
    total += consecutive * (consecutive + 1) / 2;
    total %= MODULO;
    return total;
}

###Rust

impl Solution {
    pub fn num_sub(s: String) -> i32 {
        const MODULO: i64 = 1000000007;
        let mut total: i64 = 0;
        let mut consecutive: i64 = 0;
        for c in s.chars() {
            if c == '0' {
                total += consecutive * (consecutive + 1) / 2;
                total %= MODULO;
                consecutive = 0;
            } else {
                consecutive += 1;
            }
        }
        total += consecutive * (consecutive + 1) / 2;
        total %= MODULO;
        total as i32
    }
}

复杂度分析

  • 时间复杂度:$O(n)$,其中 $n$ 是字符串的长度。需要遍历字符串一次。

  • 空间复杂度:$O(1)$。只需要维护有限的变量,空间复杂度是常数。

❌