Leetcode 466. 统计重复个数【寻找循环节,双百提交,解释非常详细】

问题描述

由 n 个连接的字符串 s 组成字符串 S,记作 S = [s,n]。例如,["abc",3]=“abcabcabc”

如果我们可以从 s2 中删除某些字符使其变为 s1,则称字符串 s1 可以从字符串 s2 获得。例如,根据定义,“abc” 可以从 “abdbec” 获得,但不能从 “acbbe” 获得。

现在给你两个非空字符串 s1 和 s2(每个最多 100 个字符长)和两个整数 0 ≤ n1 ≤ 106 和 1 ≤ n2 ≤ 106。现在考虑字符串 S1 和 S2,其中 S1=[s1,n1]S2=[s2,n2]

请你找出一个可以满足使 [S2,M]S1 获得的最大整数 M 。

解题报告

\qquad 如果s2在S1中出现了N次,那么S2肯定在S1中出现了 N / n 2 N/ n_2 N/n2 次。

\qquad 直接暴力求解s2S1中出现的次数显然不合理。很明显,只要有解,且S1的长度足够长,那么S1中必然会存在s2的循环节

\qquad 问题的关键就变成如何求解这个循环节。
\qquad 解决方式是:计算 S1 中每个小段第一次和 s2 中哪个字符发生匹配。
\qquad 比如:s1=“abc”s2=“bac”。则S1的第一段第一次与s2中的字符发生匹配的是第一个字符,记录mp[0]=1S1的第二段第一次与s2中的字符发生匹配的是第二个字符,记录mp[1]=2S1的第三段第一次与s2中的字符发生匹配的是第一个字符,此时mp[0]已经有值,所以循环节出现了。
\qquad 为什么此时循环节出现了呢,因为一旦mp[i]有值了【只要能进行匹配,那么此时一次若干次完整的匹配已经完成了】,由于S1是小段循环的,所以S1接下来的段都会按照mp[i]第一次赋值时那样的匹配方式来进行,这样就不必继续计算了。

\qquad 所以代码的核心思想是:计算这个pattern占了S1的多少段,S1一共有多少个pattern,最后计算剩下的非pattern的字符串里能包含几个s2,最后将两者相加除以 n 2 n_2 n2 即为最终的答案了。

\qquad 时间复杂度为: O ( s 1. s i z e ( ) ∗ s 2. s i z e ( ) ) O(s1.size()*s2.size()) O(s1.size()s2.size())。虽然代码看着时间复杂度为 O ( n 1 ∗ s 2. s i z e ( ) ) O(n_1*s2.size()) O(n1s2.size())。实际上,因为S1的每一段对应着s2的某一位字符,那么在进行s2.size()段分析之后,会结束循环。根据鸽巢原理(又称抽屉原理),在进行s2.size()+1段之后,必定有s2的某一位字符重复出现了。
\qquad 空间复杂度为: O ( s 2. s i z e ( ) ) O(s2.size()) O(s2.size())

实现代码

class Solution {
public:
    int getMaxRepetitions(string s1, int n1, string s2, int n2) {
        vector<int> repeatCnt(s2.size() + 2, 0);
        unordered_map<int,int>mp;
        int j = 0, cnt = 0;
        for (int k = 1; k <= n1; ++k) {
            for (int i = 0; i < s1.size(); ++i) {
                if (s1[i] == s2[j]) {
                    ++j;
                    if (j == s2.size()) {  
                        j = 0;
                        ++cnt;
                    }
                }
            }
            repeatCnt[k] = cnt;
            if(mp.count(j)){
                int start=mp[j];
                int interval = k - start;
                int repeat = (n1 - start) / interval;
                int patternCnt = (repeatCnt[k] - repeatCnt[start]) * repeat;
                int remainCnt = repeatCnt[start + (n1 - start) % interval];
                return (patternCnt + remainCnt) / n2;
            }
            else
                mp[j]=k;

            //if(k>s2.size()) break;
        }
        return cnt / n2;
    }
};

参考资料

[1] Leetcode 466. 统计重复个数
[2] [LeetCode] 466. Count The Repetitions 计数重复个数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值