整除子串
作者:互联网
整除子串
给定一个由数字组成的字符串 $s$,请你计算能够被 $4$ 整除的 $s$ 的子串数量。
子串可以包含前导 $0$。
例如,如果 $s$ 为 124 ,则满足条件的子串有 $4$ 个: 12 , 4 , 24 , 124 ;如果 $s$ 为 04 ,则满足条件的子串有 $3$ 个: 0 , 4 , 04 。
输入格式
一个由数字组成的字符串 $s$。
输出格式
一个整数,表示满足条件的子串数量。
数据范围
前 4 个测试点满足 $1 \leq \left| s \right| \leq 10$。
所有测试点满足 $1 \leq \left| s \right| \leq 3 \times {10}^{5}$。
输入样例1:
124
输出样例1:
4
输入样例2:
04
输出样例2:
3
输入样例3:
5810438174
输出样例3:
9
解题思路
$4 \mid n$的充分必要条件是$4$能整除$n$的末两位。把$n$拆成两部分,末两位为$ab$,前面剩余的位为$m$,即有$n = mab$,那么有$n = m \times 100 + ab$,其中不管$m$是什么,$4$都能整除$m \times 100$,因此$4 \mid n$等价于$4 \mid ab$。
因此我们可以枚举区间的右端点,看前面有多少个左端点使得这个区间组成的数能够被$4$整除,只需要看最后两位数$i-1$和$i$组成的数能否被$4$整除,如果可以被$4$整除,那么左端点可以取前面任何一个值(从$0 \sim i - 1$,共$i$个)。如果两位数不能被$4$整除,那么无论前面取什么都不能被$4$整除,即能够被$4$整除的子串数为$0$。还需要特判一下字串长度为$1$的情况,只需要看这一位能否被$4$整除。
AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N = 3e5 + 10; 5 6 char str[N]; 7 8 int main() { 9 scanf("%s", str); 10 11 long long ret = 0; 12 for (int i = 0; str[i]; i++) { 13 if ((str[i] - '0') % 4 == 0) ret++; // 特判长度为1的子串 14 // 子串长度大于1的情况,看末两位能否被4整除 15 if (i && ((str[i - 1] - '0') * 10 + (str[i] - '0')) % 4 == 0) ret += i; 16 } 17 18 printf("%lld", ret); 19 20 return 0; 21 }
这题还可以用dp来做。一开始太死板了,没想到当前状态是可以从上一次的哪个状态转移过来,其实可以反过来思考,可以通过当前状态可以转移到下一个的哪个状态来进行状态的计算。
AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long LL; 5 6 const int N = 3e5 + 10; 7 8 char str[N]; 9 int f[N][4]; 10 11 int main() { 12 scanf("%s", str + 1); 13 14 for (int i = 0; str[i + 1]; i++) { 15 f[i + 1][(str[i + 1] - '0') % 4]++; // 长度为1的子串 16 for (int j = 0; j < 4; j++) { 17 f[i + 1][(j * 10 + str[i + 1] - '0') % 4] += f[i][j]; // 从当前状态转移到下一个状态 18 } 19 } 20 21 LL ret = 0; 22 for (int i = 1; str[i]; i++) { 23 ret += f[i][0]; // 结果是枚举所有以右端点结尾,构成的数模4为0的字串数量 24 } 25 printf("%lld", ret); 26 27 return 0; 28 }
参考资料
AcWing 4426. 整除子串(AcWing杯 - 周赛):https://www.acwing.com/video/3897/
标签:子串,10,int,ret,str,整除 来源: https://www.cnblogs.com/onlyblues/p/16323287.html