每日一题 LeetCode

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/count-the-repetitions/solution/tong-ji-zhong-fu-ge-shu-by-leetcode-solution/

统计重复个数(hard*^*)

题目描述

每日一题 LeetCode_第1张图片

题解

找出循环节
每日一题 LeetCode_第2张图片
根据鸽笼原理,最多只要找过 |s2| + 1 个 s1,就一定会出现循环节。

复杂度分析
时间复杂度: O ( ∣ s 1 ∣ ∗ ∣ s 2 ∣ ) O(|s1|*|s2|) O(s1s2)。我们最多找过 |s2| + 1 个 s1,就可以找到循环节,最坏情况下需要遍历的字符数量级为 O ( ∣ s 1 ∣ ∗ ∣ s 2 ∣ ) O(|s1|*|s2|) O(s1s2)
空间复杂度: O ( ∣ s 2 ∣ ) O(|s2|) O(s2)。我们建立的哈希表大小等于 s2 的长度。

代码

class Solution {
public:
    int getMaxRepetitions(string s1, int n1, string s2, int n2) {
        if(n1 == 0) return 0;
        int s1_cnt = 0, index = 0, s2_cnt = 0;
        unordered_map<int, pair<int, int>> recall; // 哈希表以S2字符串下标index为索引,存储匹配至第 s1_cnt 个 s1 的末尾,当前匹配到第 s2_cnt 个 s2 中的第 index 个字符时, 已经匹配过的s1 的个数 s1_cnt 和 s2 的个数 s2_cnt 。
        pair<int, int> pre_loop, in_loop;
        while(1){
            ++s1_cnt;
            for(char ch: s1){
                if(ch == s2[index]){
                    index++;
                    if(index == s2.size()){
                        ++s2_cnt;
                        index = 0;
                    }
                }
            }
            if(s1_cnt == n1)
               return s2_cnt/n2;
            // 出现之前index,找到循环节
            if(recall.count(index)){
                auto [s1cnt_prime, s2cnt_prime] = recall[index];
                pre_loop = {s1cnt_prime, s2cnt_prime};
                in_loop = {s1_cnt - s1cnt_prime, s2_cnt - s2cnt_prime};
                break;
            }
            else
                recall[index] = {s1_cnt, s2_cnt};
        // ans 存储的是 S1 包含的 s2 的数量,考虑的之前的 pre_loop 和 in_loop
    }
        int ans = pre_loop.second + (n1 - pre_loop.first) / in_loop.first * in_loop.second;
        // S1 的末尾还剩下一些 s1,我们暴力进行匹配
        int rest = (n1 - pre_loop.first) % in_loop.first;
        for (int i = 0; i < rest; ++i) {
            for (char ch: s1) {
                if (ch == s2[index]) {
                    ++index;
                    if (index == s2.size()) {
                        ++ans;
                        index = 0;
                    }
                }
            }
        }
        // S1 包含 ans 个 s2,那么就包含 ans / n2 个 S2
        return ans / n2;
    }
};

小结

map与unordered_map内部实现机理
map: map内部实现了一个红黑树(红黑树是非严格平衡二叉搜索树,而AVL是严格平衡二叉搜索树),红黑树具有自动排序的功能,因此map内部的所有元素都是有序的,红黑树的每一个节点都代表着map的一个元素。因此,对于map进行的查找,删除,添加等一系列的操作都相当于是对红黑树进行的操作。map中的元素是按照二叉搜索树(又名二叉查找树、二叉排序树,特点就是左子树上所有节点的键值都小于根节点的键值,右子树所有节点的键值都大于根节点的键值)存储的,使用中序遍历可将键值按照从小到大遍历出来。
优点:有序性;
缺点:空间占用率高。
对于有顺序要求的问题,map效率相对较高。

unordered_map: unordered_map内部实现了一个哈希表(也叫散列表,通过把关键码值映射到Hash表中一个位置来访问记录,查找的时间复杂度可达到O(1),其在海量数据处理中有着广泛应用)。因此,其元素的排列顺序是无序的。哈希表详细介绍
优点: 查找速度快;
缺点: 哈希表的建立比较耗费时间
适用处:对于查找问题,unordered_map更高效。

对于unordered_map或unordered_set容器,其遍历顺序与创建该容器时输入的顺序不一定相同,因为遍历是按照哈希表从前往后依次遍历的

你可能感兴趣的:(leetcode,每日一题,算法,leetcode,数据结构)