Leetcode2207. 字符串中最多数目的子字符串

文章介绍了如何使用贪心策略和一次遍历解决LeetCode题目2207,计算给定字符串中特定子字符串(如xy)的最大出现次数,考虑了字符x和y在文本中的位置影响。

Every day a Leetcode

题目来源:2207. 字符串中最多数目的子字符串

解法1:贪心 + 一次遍历

设 pattern 的第一个字符为 x,第二个字符为 y。

根据题意,x 插入的位置越靠左,答案的个数越多;y 插入的位置越靠右,答案的个数越多。

那么 x 应置于 text 最左侧,yyy 应置于 text 最右侧。

我们可以遍历 text 来统计答案:每遇到一个 y,就累加左边 x 的个数。

注意特判 x=y 的情况。当两个字符相同时,设 x 的出现次数为 cnt,子字符串的出现次数 = cnt * (cnt + 1) / 2。

代码实现时,放 x 还是放 y 可以一次遍历求出来:

  • 放 x 在最左侧:每次遇到 y 都会多算一个 x,因此答案会多 cnt_y 个(cnt_y 是 y 在 text 中的出现次数)。
  • 放 y 在最右侧:这个 y 会与 text 的所有 x 组成子序列,因此答案会多 cnt_x 个(cnt_x 是 x 在 text 中的出现次数)。

代码:

/*
 * @lc app=leetcode.cn id=2207 lang=cpp
 *
 * [2207] 字符串中最多数目的子字符串
 */

// @lc code=start
class Solution
{
public:
    long long maximumSubsequenceCount(string text, string pattern)
    {
        char x = pattern[0], y = pattern[1];
        if (x == y)
        {
            long long cnt = count(text.begin(), text.end(), x);
            return cnt * (cnt + 1) / 2;
        }

        long long ans = 0;
        int cnt_x = 0, cnt_y = 0;
        for (char &c : text)
        {
            if (c == x)
                cnt_x++;
            else if (c == y)
            {
                cnt_y++;
                // 每遇到一个 y,就累加左边 x 的个数
                ans += cnt_x;
            }
        }
        return ans + max(cnt_x, cnt_y);
    }
};
// @lc code=end

结果:

在这里插入图片描述

复杂度分析:

时间复杂度:O(n),其中 n 是字符串 text 的长度。

空间复杂度:O(1)。

<think>嗯,我现在得仔细看看这个问题。题目是说,给定一个整数num和k,要找出num的所有长度为k的子字符串中,能整除num的数量。这个叫做k美丽值。那首先我得理解清楚问题。 首先,子字符串是连续的,长度为k的数字部分。比如num是240,k是2的话,子字符串就是"24"和"40"。然后这两个子字符串对应的数字分别是24和40。这时候要看这两个数是否能整除240。比如24的话,240除以24是10,能整除;而40的话,240除以40是6,也能整除。所以这时候k美丽值是2。 那问题转化为:如何提取num的每一位数字,得到所有长度为k的子字符串对应的数值,然后检查是否能整除num。需要注意,子字符串可能有前导零,比如num=430043,k=2的话,中间的子字符串"00"对应的数值是0,这时候0是不能作为除数的,所以这种情况不计入结果。 那问题的关键点有两个:如何高效地遍历所有长度为k的子字符串,以及如何判断这些子字符串对应的数值是否能整除num。 首先,我得考虑如何将num转换为字符串,这样方便提取子字符串。例如,将num转换为字符串s,然后遍历所有i,从0到len(s)-k的位置,取s.substr(i, k)作为子字符串。然后将该子字符串转换为整数,如果这个整数不为零,并且num能整除它,那么就计数加一。 但这里需要注意几个边界条件: 1. num可能非常大,比如超过int的范围。例如当num是很大的数时,其子字符串可能也会很大。比如num是一个30位的数,那么k=10的话,子字符串转换成的整数可能超过long long的范围。这时候处理起来会有问题,因为无法用常规的整数类型来保存。 这时候应该如何处理?题目中的num本身是作为整数输入的,所以可能当num非常大时,转换成字符串处理可能更合理。但这时候子字符串对应的数值可能无法用基本数据类型保存,这时候是否会导致溢出? 比如,如果num是10^18级别,k=18的话,子字符串对应的数值也是同样大的,这时候在C++中用long long可能无法处理,因为long long最大是2^63-1,约9e18。这时候可能需要用字符串转换为大数来进行除法运算?或者有没有更聪明的办法? 不过题目中的条件允许子字符串对应的数值为零的情况吗?不行,因为题目中说明,0不能整除任何数。所以当子字符串对应的数值是0时,直接跳过。 但问题中的num可能本身包含前导零吗?比如num是作为整数输入的,所以不可能有前导零。例如,如果num是024这样的写法,在C++中是无效的。所以num的字符串表示不会有前导零,但子字符串可能有前导零,比如num是240,子字符串是"04"对应的4,这时候需要处理吗? 子字符串中的前导零是允许的,比如题目中明确说明允许有前缀0。所以即使子字符串是"04",对应的数值是4,只要4能整除num的话,就算一个。 那现在问题分解: 步骤一:将num转换为字符串s。然后检查s的长度是否至少为k。如果s的长度小于k,那么无法有长度为k的子字符串,直接返回0。 步骤二:遍历s的所有起始位置i(从0到s.size()-k),截取长度为k的子字符串sub。 步骤三:将sub转换为对应的数值val。如果转换过程中出现数值过大,怎么办?例如当k很大时,比如k=20,val可能超过long long的范围。这时候如何处理? 这时候,可能需要用字符串转换为大整数,但这样的话,计算是否num能被val整除会比较困难。或者是否有其他方法? 或者,题目中的num本身可能是一个很大的数,例如当num是10^18的时候,转换为字符串可能长度为18,这时候如果k=18的话,子字符串就是整个数,所以val等于num,此时num%val等于0,所以计数加一。但这时候转换后的val就是num,所以没有问题。 但是如果k是比num的位数小的情况,比如num是999999999999999999(18个9),k=10的话,子字符串对应的数值可能达到9999999999,这在long long范围内(最大是9e18左右,所以没问题)。所以对于k较大的情况,比如k=18,此时val可能等于num,所以当k等于s的长度时,子字符串只能是num本身,所以只有当num本身不为零时,才会计数加一。但根据题目条件,num本身是一个整数,所以不可能为零吗?或者num可以是零? 但题目中的num的输入是整数,所以当num是零的时候,所有子字符串对应的数值都是零,这时候根据条件,0不能作为除数,所以所有情况都不计入,所以此时k美丽值为0。 综上,可能只有当k不超过s的长度时,才可能有解。否则返回0。 那现在,如何处理当k等于s的长度的情况?此时子字符串只能是整个num,所以val等于num。这时候num除以val等于1,余数为0,所以计数加一。所以此时k美丽值为1。 那回到问题,如何处理子字符串转换为数值的问题? 例如,在C++中,如果num是很大的数,比如超过1e18的话,当k很大的时候,子字符串对应的数值可能无法用long long保存。这时候如何处理? 例如,假设num是10^20,k=20,那么子字符串对应的数值是1e20,而long long最大值是约9e18,这时候转换会溢出,导致结果不正确。这时候如何处理? 这里可能的解决方法是,用字符串处理的方式,将子字符串对应的数值转换为字符串,然后计算这个数值是否能整除num。然而,如何比较两个大数的除法是否余数为零? 或者,可以将问题转换为数学问题:子字符串对应的数值是val,而num是否能被val整除。即num % val == 0? 当num和val都是非常大的时候,直接计算这个余数可能比较困难,但注意到num本身在题目中是作为整数输入的,所以我们可以用字符串来转换val,然后用大数取余的方法来计算余数吗? 或者,有没有更简单的方式? 例如,假设num是给定的整数,可能非常大,但题目中给出的num在C++中可能只能用字符串来保存?或者,题目中的num是作为整数输入的,所以可能其范围是有限的? 比如题目中的num的类型是否是整数?比如,在C++中,如果用户输入的是非常大的num,比如超过long long的范围,那么可能无法正确存储。所以这可能意味着题目中的num在输入时可能是一个字符串,或者题目中的num的范围在可以处理的范围内? 这时候可能需要更仔细地看问题描述。原题中的输入是整数num和k。但实际中,当num非常大时,例如超过1e18的话,那么C++的long long也无法保存。这时候,必须将num作为字符串处理。 所以正确的做法是,将num视为字符串,这样即使num很大,也能处理。例如,num的字符串形式可能非常长,而k可能也比较大,这时候子字符串对应的数值可能无法用常规的整数类型保存。这时候,如何判断num是否能被子字符串对应的数值整除? 这似乎变得比较复杂,因为需要处理大数运算。 但原题给出的例子可能假设num可以用常规的整数类型处理,所以可能不需要处理非常大的数。但题目中没有明确说明这一点。例如,LeetCode上的类似题目可能要求处理较大的数值,所以这时候必须考虑如何处理大数问题。 比如,在LeetCode 2269题的示例中,num可以大到很大,例如示例中的num=430043,k=2。这时候,将num转换为字符串处理是比较直接的方式,但如何处理val的转换? 这个时候,可能需要将num作为字符串处理,同时将子字符串转换为数值的时候,如果数值太大,无法用long long保存,那么如何处理? 这时候可能需要用字符串转换为大数,然后计算num是否可以被这个大数整除。然而,在C++中,处理大数的取余操作可能比较复杂。 或者,是否有更聪明的数学方法? 或者,假设num的值在程序中是可以用long long保存的。例如,num的值在1e18以内,那么子字符串对应的数值最多也是1e18(当k=18时),这时候用unsigned long long可能保存。这样,我们可以将num转换为long long类型,然后对于每个子字符串转换为对应的数值val,如果val超过了long long的范围,则无法处理。但这种情况可能只有当k>18的时候才会发生,因为long long的最大值是9e18左右,而k位数的最大值是10^k-1。当k=19时,最大值是1e19-1,这超过了long long的范围。这时候,如何处理? 这时候,可能题目中的测试用例不会涉及这样的情形,或者需要另寻方法。 但原题并没有明确说明这一点。所以需要找到一种既能处理大数,又不需要复杂的大数运算的方法。 那么,现在回到问题,如何高效地解决这个问题? 首先,将num转换为字符串s。如果s的长度小于k,返回0。 否则,遍历所有可能的子字符串,每个子字符串转换为数值val。如果val为0,跳过。否则,检查num是否能被val整除。若可以,计数加一。 问题在于,当num非常大时,比如num是长度为1e5的字符串,此时无法将其转换为数值,这时候如何计算num%val? 这时候,可以利用取余的性质。例如,num作为一个字符串,我们可以逐位处理,计算其对val的余数。如果余数为0,则说明num可以被val整除。 例如,假设val是某个整数,那么可以将num字符串的每一位数字依次处理,计算num mod val的值。如果结果为0,则说明可以被整除。 例如,num是字符串"12345",val是3,那么我们可以计算12345 mod3的值。而这个过程可以通过逐位计算: 初始余数0。对于每个数字d,余数 = (余数 *10 + d) % val。 这样,不管num有多大,只要val在整型范围内,就可以计算余数。但如果val非常大,比如超过1e18的话,这可能会导致溢出,因为中间结果可能超过long long的范围。 例如,假设val是1e18,那么在计算过程中,余数会可能达到(1e18 *10 +9),这会超过long long的范围(约9e18)。这时候会导致溢出,得到错误的结果。 所以,这种方法的限制是,val必须足够小,使得中间计算不会溢出。或者,在C++中使用更大的数据类型,比如__int128,但这可能不是所有编译器都支持。 综上,这个问题需要处理两种情况: 1. 当val可以用常规的整数类型(比如long long)保存时,可以使用逐位取余法来判断num是否能被val整除。 2. 当val太大,无法保存时,这种情况下,无法处理,但此时val可能大于num的绝对值,此时val无法整除num,除非val等于num。例如,当子字符串是num本身时,val等于num,此时num%val=0,所以计入结果。其他情况下,如果val>num的话,无法整除,除非val等于num。所以这种情况只有在k等于num的位数时才可能发生。此时,只有当子字符串等于num时才可能满足条件。 那这样的话,当k等于num的位数时,结果只能是1或0。例如,num是123,k=3,那么子字符串只能是123,此时val=123,num/val=1,所以余数为0,计数加1。但是如果num是负数的话?题目中的num是否是正整数? 题目中的描述没有说明num的正负,但根据样例,比如给出的num=240,k=2,得到的结果是2。所以可能题目中的num是正整数? 或者,题目中的num是绝对值的正整数?例如,如果num是负数的话,那么子字符串对应的数值可能也是负数?但子字符串是数字字符串,所以转换为数值时会是正数吗? 例如,num是-240,那么转换为字符串是"-240",长度为4。k=2的话,可能的子字符串是"-2"、"24"、"40"?但是这里的子字符串是连续的字符序列,所以第一个子字符串是"-2",第二个是"24",第三个是"40"。但是转换为数值的话,"24"是24,"40"是40,而"-2"是-2。这时候,判断num % val是否为0?即-240 % (-2)的结果是0吗?在C++中,取模运算的结果符号取决于被除数,所以-240 % -2等于0。但是题目中的条件允许负数的子字符串吗? 原题中的子字符串能整除num的条件,可能要考虑正负的问题。例如,如果num是负数,而val是正数,那么是否能整除? 例如,num=-240,val=24。则-240 /24 =-10,余数是0。所以应该被计数。但是原题中给出的示例都是正数,所以可能题目中的num是正整数? 或者,题目中的条件是否允许num为负数? 这可能需要看原题的具体条件。假设num是正整数的话,处理会更简单。否则,需要考虑负数的处理。 综上,可能题目中的num是正整数,所以不需要考虑负数的情况。这可能由题目中的示例所暗示。 现在回到问题,假设num是正整数,转换为字符串后,处理所有可能的子字符串。 那么,如何处理大数的情况? 例如,假设num的字符串形式是s,长度为n。当子字符串对应的val很大时,无法用long long保存,那么如何判断num % val ==0? 这时候,可以采用逐位取余的方法: 对于给定的val,我们可以将s表示的num逐位处理,计算余数。例如,对于每个字符c in s,对应的数字是d = c - '0'。余数r = (r *10 +d) % val。如果最终r ==0,则num % val ==0。 这种方法的时间复杂度是O(n),其中n是num的位数。对于每个子字符串,需要两次O(n)操作:一次是转换子字符串为val,另一次是计算num%val。 但如何转换子字符串为val?如果子字符串的长度k很大,比如k=20,那么转换为val可能无法保存为long long,这时候如何得到val的值? 这时候,只能将子字符串视为字符串,然后将其转换为大数。但这时候,计算num%val的时候,val可能很大,无法保存到常规的整数类型中,这导致无法用逐位取余的方法。 这时候,如何处理? 这个问题似乎比较困难。可能需要找到一种方法,不需要显式地转换子字符串为数值,而是直接处理字符串,计算余数。 或者,可以尝试将子字符串对应的数值转换为一个可能的数值类型,如果转换失败(比如溢出),则判断该子字符串是否等于整个num的字符串,此时val等于num,所以余数为0;否则,该val一定大于num,所以无法整除。或者,当子字符串的长度等于num的字符串长度时,val等于num,此时余数为0。否则,如果val的位数等于num的位数,但子字符串不同,则val不等于num,所以余数不为0。这可能需要更仔细的分析。 例如,当num是123,k=3,则子字符串只能是"123",对应的val是123,等于num,所以计数加一。 如果num是12345,k=3,子字符串为"234",此时val是234,但num是12345,比val大,所以需要判断12345%234是否为0。 这时候,如果val可以用long long保存,就可以用逐位取余法。否则,当val无法保存时,该怎么办? 这种情况下,只能处理两种情况: 1. 子字符串对应的val可以转换为long long,此时判断num%val是否为0。 2. 子字符串对应的val无法转换为long long,此时: a. 如果子字符串的长度等于整个num字符串的长度,那么val等于num,此时余数为0,计数加一。 b. 否则,val的长度等于k,而num的字符串长度大于k,所以val的值可能大于或等于num。例如,当num是1000,k=3,那么子字符串是"100"(val=100),此时1000%100=0。或者,子字符串是"000"(val=0)不计入。 所以,当val无法保存到long long时,如何处理? 比如,val的位数k很大,比如k=20,而num的位数是30。此时,如果val对应的数值比num大,则num%val可能等于num本身,所以只有当val等于num时才会余数为0。否则,若val大于num,则无法整除。 但如何判断val是否等于num? 可以通过比较子字符串是否等于整个num的字符串。例如,当k等于s.size()时,子字符串就是整个字符串,此时val等于num。否则,当k < s.size()时,子字符串的位数较小,对应的数值可能比num小,也可能更大? 比如,num是1000,k=3,子字符串是"000",val是0,不计入。或者,当num是999,k=3,子字符串是"999",val等于num,此时计数加一。 综上,当无法将子字符串转换为数值时(比如数值太大),如何处理? 在这种情况下,可能有以下几种情况: - 子字符串对应的数值等于num:此时计数加一。 否则,无法判断是否num%val等于0,因为val无法保存,无法计算余数。 但这种情况可能只有在子字符串等于整个num的字符串时才会成立。否则,当k < len(s),那么子字符串的长度小于num的位数,其对应的数值的位数最多等于k。而num的位数更大,所以当k < len(s)时,val的位数最多k,而num的位数大于k,所以val的值可能比num小或者大? 比如,num=1000,k=3,子字符串是"000",val=0不计入。其他子字符串如"100"(val=100),1000%100=0。或者,num=1234,k=3,子字符串是"234",val=234,1234%234= 1234 - 5*234=1234-1170=64,余数不为0。 当无法将val转换为数值时,比如val的数值超过了long long的范围,那么如何处理? 比如,num是一个长度为30位的数,k=20,子字符串对应的数值是20位,可能超过long long的范围(最大是9e18,即19位)。 此时,可以尝试将子字符串转换为字符串,然后num的字符串进行比较,如果子字符串等于整个num字符串的话,则说明val等于num,此时余数为0。否则,当k < len(s)时,val的数值可能无法保存,此时无法判断是否num%val==0,除非val等于num的某个因数。这可能无法处理。 因此,在这种情况下,问题可能无法正确解决。这可能意味着,在题目给出的约束条件下,val的大小可能不会超过long long的范围,或者需要另寻他法。 综上,可能的解决方案如下: 将num转换为字符串s。遍历所有可能的长度为k的子字符串,对于每个子字符串: - 如果子字符串转换为数值val时,val为0,跳过。 - 否则,如果val可以转换为long long类型,则计算num%val是否等于0。如果num太大无法转换为long long,则num必须作为字符串处理,逐位计算余数。 或者,无论num的大小,都用逐位取余的方法来计算余数。 那如何将num作为字符串处理,逐位计算余数? 假设我们有num的字符串表示s_num,要计算s_num对应的数值对val的余数。此时,val是一个可能很大的数,但可能无法保存到long long中。这时候如何处理? 这时候,逐位处理的方法是否可行? 例如,余数初始化为0。对于每个字符c in s_num: 余数 = (余数 * 10 + (c - '0')) % val; 这只有当val可以保存到整型变量中的时候才有效。因为如果val太大,超过整型的范围,那么无法进行取模运算。例如,在C++中,如果val是1e20,而变量val的类型是long long,则无法保存,导致溢出。 所以,这种方法只能在val可以保存为整型时有效。 所以,如何处理当val超过long long范围的情况? 这时候,可能无法处理,只能认为这种情况下的余数无法计算,从而无法判断是否num%val==0。这将导致可能漏掉某些情况。 综上,可能在题目给定的约束下,k的值不会太大,从而val可以保存在long long中。例如,k<=18。这样,子字符串最多是18位,可以转换为unsigned long long类型(最大为18446744073709551615,约1.8e19)。 那么,在代码中,可以将子字符串转换为unsigned long long类型的val。如果转换失败(即子字符串的数值超过unsigned long long的范围),则跳过该子字符串? 或者,在转换时,如何处理溢出的问题? 例如,使用stoull函数,并捕获异常。但是,C++字符串转换函数在转换溢出时会返回ULLONG_MAX,并设置errno。例如,可以使用strtoull函数,并检查是否溢出。 这可能需要更复杂的处理。例如,在C++中,可以尝试将子字符串转换为unsigned long long: string sub = s.substr(i, k); // 检查是否全为数字? // 转换sub为val: char* endptr; unsigned long long val = strtoull(sub.c_str(), &endptr, 10); if (endptr != sub.c_str() + sub.size()) { // 转换失败,可能包含非数字字符? // 但原num是整数,所以子字符串应该都是数字字符。所以这种情况不会出现? } 然后,检查是否转换时有溢出。例如,当转换后的val等于ULLONG_MAX且errno == ERANGE,说明转换溢出,此时val无法保存到unsigned long long中。 此时,如何处理这种情况? 在这种情况下,val无法保存到unsigned long long中,所以无法计算num%val是否为0。这时候,只能认为该子字符串对应的val太大,无法处理,所以是否计入结果? 例如,当子字符串对应的val是num本身时,比如当k等于s.size()时,子字符串就是整个num的字符串,此时val等于num。此时,如果val溢出,无法保存到unsigned long long中,则无法判断。但此时,原题中的num可能本身无法保存到unsigned long long中,那么如何处理? 这似乎陷入了一个困境。 综上,可能问题中的num和k的条件是,val可以用unsigned long long保存。或者,题目中的测试用例不会出现这种情况。或者,题目中的num在输入时作为整数,所以其大小在C++的long long范围内。 那假设num的范围可以用long long保存,那么代码可以这样处理: 将num的绝对值转换为字符串s(因为负数的情况可能不需要处理,或者需要单独处理)。然后,遍历所有长度为k的子字符串,转换为数值val。如果val为0,跳过。否则,检查原num能否被val整除(注意处理负数的情况)。 例如,对于num=240,k=2: 子字符串是"24"和"40"。24和40都整除240,所以计数为2。 对于num=430043,k=2: 子字符串有"43"(430043%43?43*10000=430000,余43,所以430043%43=43%43=0?等等。可能计算的时候需要考虑。 或者,需要更精确的计算。 综上,代码的步骤大致如下: 1. 将num转换为字符串s。注意处理负数的情况。例如,如果num是负数,那么s的第一个字符是'-',此时子字符串可能包含该符号吗? 根据题目中的描述,子字符串是num中的连续字符序列。例如,num是-123,k=2,则可能的子字符串是"-1"、"12"、"23"。其中,"-1"转换为数值是-1。这时候,若num是-123,能否被-1整除?-123 / (-1)=123,余数0。所以这种情况下,这个子字符串是有效的。 所以,当num是负数时,子字符串可能包含负号,此时val可能为负数。此时,需要将子字符串转换为带符号的整数。但题目中的条件允许吗? 原题中的例子都是正数,但问题描述中并未明确说明num的正负。所以在代码中,可能需要处理负号的情况。 但如何判断子字符串是否包含负号?例如,当num是-123时,子字符串的长度k如果大于1的话,可能的子字符串是否包含负号? 例如,k=3的话,子字符串可能是"-12"、"123"。其中,第一个子字符串转换为-12,第二个转换为123。此时,判断-123是否能被-12或123整除。 综上,当num是负数时,子字符串可能包含负号,也可能不包含。例如,当k=3,子字符串起始位置是0的话,得到的子字符串是"-12",此时转换为数值-12。当起始位置是1的话,得到"12"对应的12。 所以,处理负数的情况比较复杂。但原题中的例子可能不涉及负数,所以可能题目中的num是正整数? 或者,在问题中,num可以是负数? 根据题目中的条件,允许子字符串有前缀0。例如,子字符串"04"会被转换为4,仍然要判断是否能整除num。例如,num是40,那么子字符串"04"转换为4,40%4=0,所以计数加一。 所以,在代码中,如何处理num的正负? 假设num可以是正数或负数,那么需要考虑以下几点: 1. 子字符串转换为数值时,可能带有负号?只有当子字符串的起始位置包含负号时才能得到负数。 例如,num是-1234,转换为字符串是"-1234"。当k=2时,可能的子字符串是"-1"、"12"、"23"、"34"。其中,第一个子字符串转换为-1,其他转换为12、23、34。这时,需要检查num%(-1)、num%12等。 但num%(-1)的余数是0吗?在C++中,取模运算的结果符号除数有关。例如,-1234 % (-1)等于0。所以,若子字符串对应的val是负数,那么计算num%val时,结果是否为0? 因此,在代码中,对于每个子字符串转换为数值val,如果val非零,则判断num % val是否等于0。这需要考虑val的正负。 但如何正确处理这种情况? 例如,num= -240,子字符串是"24",val=24。num%val等于 (-240) %24。在C++中,(-240)%24等于0吗?因为24 * (-10) =-240。所以余数是0。所以,这种情况下,该子字符串会被计数。 所以,无论val是正还是负,只要其绝对值能整除num的绝对值,且val不为零,那么num%val就会等于0。 因此,可以将val的绝对值作为除数,并判断num是否能被其整除。或者,在代码中,无论val的正负,直接计算num%val是否等于0。 例如,在C++中: int num = -240; int val = 24; int rem = num % val; // rem = (-240) %24 = 0. 所以,在这种情况下,余数为0,会被计数。 因此,无论val是正还是负,只要其绝对值能整除num的绝对值,并且val不等于0,那么余数为0,会被计数。 因此,在代码中,可以将子字符串转换为有符号的long long类型,并判断val是否为0,然后计算num%val是否等于0。 综上,代码的大体思路如下: 1. 将num转换为字符串s。注意,当num为负数时,s包含负号。 2. 检查s的长度是否小于k。如果是,返回0。 3. 遍历每个可能的起始位置i,i的范围是0 <= i <= s.size() -k。 a. 提取子字符串sub = s.substr(i, k)。 b. 转换sub为long long类型的val。处理可能的符号: - 如果sub的第一个字符是'-',那么剩下的字符必须是数字,且sub的长度大于1? - 例如,子字符串是"-0",转换后是0,此时val=0,不计入。 c. 如果转换后的val为0,跳过。 d. 否则,检查num%val是否等于0。若等于,计数加一。 4. 返回计数。 现在,问题是如何转换子字符串为long long,并且处理可能的溢出? 例如,子字符串是"12345678901234567890",这显然超过了long long的范围。在C++中,如何检测这种情况? 可以使用字符串到数值的转换函数,并检查是否溢出。例如,使用stoll函数,并捕获异常。或者,使用strtoll函数,并检查errno。 例如: string sub = ...; char* endptr; long long val = strtoll(sub.c_str(), &endptr, 10); if (endptr != sub.c_str() + sub.size()) { // 转换失败,比如子字符串包含非数字字符(除了可能的第一个负号) continue; } if (errno == ERANGE) { // 转换结果超出范围,无法表示 // 此时,val可能是LLONG_MIN或LLONG_MAX // 如何处理? // 如果val的绝对值大于num的绝对值,那么无法整除,除非等于num本身。 // 例如,当子字符串的长度等于s的长度,且转换后的val溢出,但子字符串等于s,则val等于num,此时num%val=0. // 所以,需要判断这种情况。 } 否则,如果val为0,跳过。 否则,判断num%val ==0? 但是,当转换后的val溢出,无法保存到long long中时,如何处理? 例如,子字符串的长度k=20,而long long的最大值是9e18左右,此时转换后的val可能超出范围。此时,val可能被设置为LLONG_MAX或LLONG_MIN,并且errno会被设置为ERANGE。 在这种情况下,如何判断该子字符串对应的数值是否等于num? 例如,当子字符串等于s本身时,此时val应该等于num。如果此时转换失败(比如溢出),则无法直接比较。但可以通过比较子字符串和s是否相等来判断? 例如,如果子字符串的长度等于s的长度,且子字符串等于s,则此时val等于num。此时,不管转换是否溢出,都可以认为val等于num,所以余数为0,计数加一。 否则,如果子字符串的长度等于s的长度,但子字符串不等于s,那么转换后的val可能溢出,但此时val不等于num,所以余数不为零。 其他情况下,子字符串的长度小于s的长度,此时转换后的val可能溢出,无法处理。此时,可以认为val的绝对值比num的绝对值大吗? 例如,num的字符串是"1234567890",k=5,子字符串是"34567",val=34567。而num=1234567890,此时34567 < 1234567890。所以,当子字符串的长度小于s的长度时,其对应的数值可能比num小,也可能更大? 比如,子字符串的长度是k,而s的长度是m。当k < m时,子字符串对应的数值可能比num小或大。例如,num=999,子字符串k=2可能得到99,小于num。或者,num=100,子字符串k=2得到10,小于100。或者,num=1234,k=3的子字符串是"234",val=234 < 1234。但是如果子字符串是"999",而num是500,那么val=999>500,无法整除。 综上,当子字符串的长度小于s的长度时,对应的val可能大于或小于num。此时,如果转换时溢出,无法判断val是否等于num,或者无法计算余数。所以这种情况下,可能无法正确处理。 因此,可能的解决方案是: 对于每个子字符串: 1. 尝试将其转换为long long类型的val。 a. 如果转换成功,并且val !=0,则计算num%val是否等于0。 b. 如果转换失败(由于溢出): i. 检查子字符串是否等于整个s。例如,k等于s的长度。此时,val等于num,所以计数加一。 ii. 否则,无法判断val是否能整除num,所以跳过。 这需要判断两种情况: - 子字符串转换后的val是否溢出,并且是否等于整个s。 例如,当k等于s的长度时,子字符串只能是整个s。此时,无论转换是否溢出,只要val等于num,则余数为0。否则,余数不为0。 所以,当转换溢出时,需要判断子字符串是否等于整个s。如果是,则计数加一。否则,无法处理,跳过。 这可能需要将num转换为字符串,然后比较子字符串和s是否相等。 综上,代码的步骤可以修改为: 遍历每个子字符串sub: 1. 如果sub的长度等于s的长度,则比较sub和s是否相等。如果相等,则val等于num,所以计数加一。否则,跳过。 2. 否则,尝试将sub转换为long long val: a. 如果转换成功: i. 如果val ==0,跳过。 ii. 否则,如果num %val ==0,计数加一。 b. 如果转换失败(溢出): 跳过。 这样处理是否正确? 例如,当子字符串的长度等于s的长度,并且等于s,则说明val等于num,所以num%val ==0,计数加一。 否则,当子字符串的长度等于s的长度,但不等于s,则说明val不等于num,所以无法整除。 对于其他情况(子字符串的长度小于s的长度),只有转换成功的val才会被处理。 这可能无法覆盖所有情况,例如,当子字符串的长度小于s的长度,但转换时溢出,但val的数值等于num。例如,num是1000000000000000000(1e18),而子字符串是"1000000000000000000",但k等于s的长度。这已经被情况1处理。 当k小于s的长度时,例如,num=1000000000000000000,s的长度是19,k=19的话不可能,此时k必须等于s的长度才能处理。 所以,这种处理方式是否正确? 例如,假设num=1000000000000000000(s的长度是19),k=19。此时,子字符串等于s,所以计数加一。此时,val转换时会溢出,因为1e18是LLONG_MAX(约9e18)的范围吗?比如,1e18的字符串是"1000000000000000000",转换成long long是否溢出? 是的,因为1e18是10^18,而LLONG_MAX是9223372036854775807,约9e18。所以转换到long long会溢出,导致errno被设置为ERANGE,val可能被设置为LLONG_MAX或LLONG_MIN。 此时,根据步骤1的处理,子字符串等于s,所以计数加一。 这样就能正确计数。 另一种情况:num是一个很大的数,无法用long long保存。例如,num的字符串形式是"123456789012345678901234567890",k=3。此时,某个子字符串如"890"转换为890,此时判断num%890是否为0。但num无法保存为long long,所以如何计算? 此时,在代码中,将num作为long long类型处理将导致溢出,无法正确计算。 这说明,当num本身超过long long的范围时,上述方法无法正确处理。因此,原问题中的num可能被限制在long long的范围内。 或者,必须将num作为字符串处理,并在计算余数时,逐位处理。 因此,为了处理大数的情况,必须将num视为字符串,并在计算余数时,使用逐位取余的方法。 那么,正确的代码可能如下: 将num转换为字符串s_num。对于每个子字符串sub: - 转换sub为字符串s_val。 - 如果s_val对应的数值为0,跳过。 - 否则,将s_val转换为数值val。如果转换成功: - 计算num % val是否为0。 - 否则(转换失败,val溢出): - 检查sub是否等于s_num。如果等于,计数加一。 - 否则,无法处理,跳过。 但问题在于,如何计算num%val。如果num无法保存为long long,那么无法直接计算num%val。这时候,必须将num视为字符串,并计算其模val的余数。但此时,val必须可以保存到某个整型中,否则无法计算。 因此,正确的处理方式如下: 对于每个子字符串sub: 1. 如果sub对应的数值val为0,跳过。 2. 否则: a. 尝试将sub转换为long long类型的val. b. 如果转换成功: i. 使用逐位取余法计算s_num对应的数值模val的余数。 ii. 如果余数为0,计数加一. c. 如果转换失败(溢出): i. 检查sub是否等于s_num。如果等于,则计数加一. ii. 否则,跳过. 这样,当sub的数值超过long long的范围时,无法处理,除非sub等于整个s_num。 在这种情况下,当子字符串等于整个s_num时,计数加一。否则,无法判断,只能跳过。 因此,这样的处理方式可能覆盖所有情况吗? 例如,当sub不等于整个s_num,且val溢出,此时无法计算余数。但此时,若val的数值大于num的数值,那么无法整除,除非val等于num。而val的数值超过long long,可能比num大,所以当sub不等于整个s_num时,无法整除,所以可以安全地跳过。 综上,这种处理方式是正确的。 现在,如何实现逐位取余? 例如,对于s_num是"123456789",val是3: 余数初始化为0。对每个字符c in s_num: 余数 = (余数 *10 + (c-'0')) % val. 这需要val是一个正数。假设val是转换后的数值的绝对值。 所以,在代码中,可以将val转换为绝对值,并计算余数。因为余数的符号不影响是否能整除的判断。 例如,当val是负数时,将其绝对值作为除数。例如,num是-240,val是-24。则-240 % (-24)等于0。此时,绝对值是24,所以计算余数时,可以取val的绝对值。 因此,在代码中: 计算余数时,取val的绝对值。 这样,不管val是正还是负,都可以正确判断是否余数为0。 综上,代码的步骤如下: 函数int kBeauty(int num, int k): 但原问题中的num可能非常大,所以必须将其作为字符串处理。 哦,原问题中的输入是整数num,所以如果num的数值非常大,超过C++的long long范围,那么将其作为整数输入可能无法正确存储。因此,可能需要将num作为字符串输入。 但原题中的函数签名可能需要是:int divisorSubstrings(int num, int k)。例如,LeetCode 2269题的输入num是整数。因此,在C++中,num的类型是int或long long吗? 比如,在LeetCode 2269题中,num的范围是1 <= num <= 1e9,而k是1 <=k <=num的位数。这时候,子字符串对应的数值不会超过1e9的位数,比如k最多是9,对应的数值最大是999,999,999,可以保存在int中。 因此,这种情况下,转换子字符串为int是可行的。 但在本题的问题描述中,用户可能输入较大的num,比如超过long long的范围,所以必须将num视为字符串处理。但原题的输入是否允许? 可能,在本题中,假设num的类型是整数,且在C++中可以保存为long long。因此,代码可以基于这个假设。 综上,现在给出代码的解决方案: 步骤: 1. 将num转换为字符串s. string s = to_string(num); 2. 如果s.size() <k → return 0. 3. 初始化计数为0. 4. 遍历i从0到s.size()-k: a.子字符串sub = s.substr(i, k). b. 检查sub是否全为数字字符(或者,如果i=0且s[0]是'-',则sub可能包含负号吗?例如,当num是负数时,子字符串可能包含负号吗?) 例如,num是-123,k=2。则可能的子字符串是"-1", "12", "23". 所以,当num是负数时,子字符串可以包含负号吗? 是的。例如,子字符串起始位置是0,长度k=2的话,就是"-1". 所以,当处理负号时,必须允许子字符串以负号开头,但仅当该子字符串的起始位置是0时才可能。 因此,在转换子字符串为val时,需要允许负号的存在,但只能在子字符串的开头。 所以,如何正确转换sub为数值? 例如,sub是"-12",可以转换为-12. sub是"12"转换为12. 所以,在转换时,允许sub以负号开头,但只能有一个负号,且后面跟着数字。 5. 转换sub为long long类型的val: a. 使用strtoll函数,检查转换是否成功。 b. 如果转换后的val为0 →跳过. c. 否则,检查num%val是否为0. d. 如果转换时出现溢出: i. 检查sub的长度是否等于s的长度,并且sub等于s → 此时val等于num,所以计数加一. ii. 否则,跳过. 这样,代码可能如下: #include <string> #include <cstdlib> #include <cerrno> #include <climits> using namespace std; class Solution { public: int divisorSubstrings(int num, int k) { string s = to_string(num); int n = s.size(); if (n < k) return 0; int count =0; for (int i=0; i<=n-k; i++) { string sub = s.substr(i, k); char* end; errno = 0; // 重置errno long long val = strtoll(sub.c_str(), &end, 10); if (end != sub.c_str() + sub.size()) { // 转换失败,无效字符 continue; } if (errno == ERANGE) { // 溢出,无法保存到long long // 检查是否sub等于整个s if (sub.size() == s.size() && sub == s) { count++; } continue; } if (val ==0) continue; if (num % val ==0) { count++; } } return count; } }; 但是,这段代码可能有问题: 例如,当num是负数时,例如num=-240,k=2。此时,s="-240".遍历i=0到3-2=1: i=0: sub="-2",转换为-2. val=-2. 判断num%val是否为0?即-240% (-2) →0,所以计数加一. i=1: sub="24",val=24 →-240%24=0 →计数加一. i=2: sub="40" →val=40 →-240%40=0 →计数加一. 所以总共有3个美丽值。但实际正确的k美丽值是多少? 原题中num是-240,k=2,子字符串是"-2"(val=-2)、"24"(24)、"40"(40). -240% (-2) →余数0 →计数加一. -240%24=0 →加一. -240%40=0 →加一. 所以美丽值是3. 但在代码中,当num是负数时,字符串是"-240",转换后的val可能为负数。 而num的类型是int,所以当num是-240时,val是-2,num%val等于-240% -2=0,所以计数加一。 因此,代码中的处理是正确的。 但是,strtoll在转换时,如果sub以负号开头,并且后面是有效的数字,那么转换得到的是负数。例如,sub是"-0",转换后的val是0,此时会被跳过。 这样,代码中的处理是正确的。 但需要注意,当num为负数时,子字符串可能包含负号,这可能导致val为负数。而num%val的计算可能正确。 综上,这段代码应该可以正确处理正负情况。 然而,上述代码在处理子字符串转换溢出时,例如当sub的长度等于s的长度,并且转换溢出,此时必须比较sub和s是否相等。例如,当num=LLONG_MAX+1,假设这个数值无法用long long保存。此时,s是"9223372036854775808",k=19。此时,sub等于s,转换溢出。此时,代码会检查sub.size() ==s.size(),并且sub等于s,所以计数加一。因此,正确。 综上,这段代码应该能正确处理所有情况。</think>要解决这个问题,我们需要找到一个整数 `num` 的 **k 美丽值**,即所有长度为 `k` 的子字符串中能整除 `num` 的数量。以下是逐步解决方案: ### 方法思路 1. **转换为字符串**:将 `num` 转换为字符串 `s`,以便处理子字符串。 2. **遍历所有子字符串**:检查所有长度为 `k` 的子字符串。 3. **转换为数值并验证**:将每个子字符串转换为数值,检查是否能整除 `num`。处理可能的溢出情况,确保正确计数。 ### 解决代码 ```cpp #include <string> #include <cstdlib> #include <cerrno> using namespace std; class Solution { public: int divisorSubstrings(int num, int k) { string s = to_string(num); int n = s.size(); if (n < k) return 0; int count = 0; for (int i = 0; i <= n - k; ++i) { string sub = s.substr(i, k); char* end; errno = 0; // 重置错误标志 long long val = strtoll(sub.c_str(), &end, 10); // 检查转换是否完全成功 if (end != sub.c_str() + sub.size()) { continue; } // 处理溢出情况 if (errno == ERANGE) { // 只有当子字符串等于原字符串时才计数 if (sub.size() == s.size() && sub == s) { ++count; } continue; } // 忽略值为0的情况 if (val == 0) { continue; } // 检查是否能整除 if (num % val == 0) { ++count; } } return count; } }; ``` ### 代码解释 1. **转换为字符串**:使用 `to_string` 将 `num` 转换为字符串 `s`。 2. **检查长度**:若字符串长度小于 `k`,直接返回 0。 3. **遍历子字符串**:使用循环遍历所有可能的子字符串起始位置。 4. **转换并验证数值**: - 使用 `strtoll` 将子字符串转换为 `long long`,处理可能的溢出(`ERANGE`)。 - 若转换失败或溢出,检查子字符串是否等于原字符串(仅当长度为 `k` 时)。 - 跳过值为 0 的子字符串。 - 检查 `num` 是否能被转换后的值整除,若可以则计数加一。 该方法有效处理了各种边界情况,包括数值溢出和负数情况,确保结果正确。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UestcXiye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值