首页 > 试题广场 >

小苯的子串删除

[编程题]小苯的子串删除
  • 热度指数:465 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
\,\,\,\,\,\,\,\,\,\,小苯有一个长度为 n 的数字串 s,他想要将 s 变为 3 的倍数。为此,他可以进行最多一次操作:
\,\,\,\,\,\,\,\,\,\,\,\,\,\,\bullet\,选择一段区间 [l, r]\ (1 \leq l \leq r \leq n),删除 s_l, s_{l+1}, \dots, s_r 这一段数位。
\,\,\,\,\,\,\,\,\,\,他想知道有多少种不同的删除区间方案,使得 s 是 3 的倍数。请你帮帮他吧。

输入描述:
\,\,\,\,\,\,\,\,\,\,第一行输入一个整数 n\left(1 \leq n \leq 2\times 10^5\right),表示字符串长度。
\,\,\,\,\,\,\,\,\,\,第二行输入一个长度为 n 且仅包含数字的字符串 s 。


输出描述:
\,\,\,\,\,\,\,\,\,\,在一行上输出输出一个整数,表示不同的删除方案数。
示例1

输入

4
1233

输出

7

说明

\,\,\,\,\,\,\,\,\,\,如果选择进行操作,则可能删除的区间有:
\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,[1, 2],则剩余 "\tt 33" ,满足是 3 的倍数。
\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,[3, 3],剩余 "\tt 123" ,满足是 3 的倍数。
\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,[1, 3],剩余 "\tt 3" ,满足是 3 的倍数。
\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,[4, 4],剩余 "\tt 123" ,满足是 3 的倍数。
\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,[3, 4],剩余 "\tt 12" ,满足是 3 的倍数。
\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,[1, 4](全部删除)则剩余 "\tt 0" ,也满足是 3 的倍数。
\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,● 不进行操作,则剩余 "\tt 1233" ,也是 3 的倍数。
\,\,\,\,\,\,\,\,\,\,则共有七种删除方案(注意,不删除/全删除也是删除方案)。

首先,将数字串s能够分出一个数字数组nums = [a0, a1, a2, a3, ..., an-1],方便后续计算。

假设nums数组所有元素之和为sum,则sum % 3就代表整个数组多出来的值,只要能够减去这个值,那么剩余的数字就可以是3的倍数(顺便减去一些是3的倍数的区间,那么最终结果也是3的倍数。

从左到右遍历,presum代表[0, i-1]的所有元素的和,同时记录presum % 3出现的次数,假设i = 0时,presum = 0

我们已经知道sum % 3的值是多少,因此,我们可以在presum中减去这个值。

此时sum - (sum % 3)剩余区间的元素之和的是3的倍数,因此组成的数字是3的倍数。

假设presum - (sum % 3)之后剩余区间叫做presum_remain

若前面有和presum_remain相同的余数的presum,则也可以算作一种方案。

因此可以删除,余数为0的部分和sum % 3的部分,这也算一种方案。

最终,写出代码:

import sys
from collections import Counter
 
line = sys.stdin.readline().strip().split()
n = int(line[0])
a = sys.stdin.readline().strip()
nums = [0] * n
for i in range(n):
    nums[i] = int(a[i])
 
m_sum = sum(nums)
cnt = Counter()
presum = 0
ans = 0
for i in range(n):
    cnt[presum % 3] += 1
    presum += nums[i]
    ans += cnt[(presum - (m_sum % 3)) % 3]
if m_sum % 3 == 0:
    ans += 1
print(ans)
发表于 2025-03-31 19:11:26 回复(0)
#include <iostream>
#include <unordered_map>
using namespace std;
int dp[2*100001][3];
int main() {
    int n;
    string s;
    long long ret=0;
    cin >> n >> s;
    int sum=0;
    for(int i=0;i<n;++i){
        sum+=s[i]-'0';
    }
    int left = sum%3;
    dp[0][(s[0]-'0')%3] = 1;
    for(int i =1;i<n;++i){
        switch ((s[i]-'0')%3) {
            case 0:
            dp[i][0] = dp[i-1][0]+1;
            dp[i][1] = dp[i-1][1];
            dp[i][2] = dp[i-1][2];
            break;
            case 1:
            dp[i][0] = dp[i-1][2];
            dp[i][1] = dp[i-1][0]+1;
            dp[i][2] = dp[i-1][1];
            break;
            case 2:
            dp[i][0] = dp[i-1][1];
            dp[i][1] = dp[i-1][2];
            dp[i][2] = dp[i-1][0]+1;
            break;
        }
    }
    for(int i=0;i<n;++i){
        ret+=dp[i][left];
    }
    if(left ==0)
        ret++;
    cout << ret << endl;
    return 0;
}

发表于 2025-03-29 15:18:56 回复(0)
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        String s = sc.next();
        int sum = 0;
        for(int i = 0;i < n;i ++ ) {
            sum += s.charAt(i) - '0';
        }
        Map<Integer, Integer> cnt = new HashMap<>();
        int presum = 0;long res = 0;
        for(int i = 1;i <= n;i ++ ) {
            cnt.put(presum % 3, cnt.getOrDefault(presum% 3, 0) + 1);
            presum += s.charAt(i - 1) - '0';
            res += cnt.getOrDefault((presum - (sum % 3)) % 3, 0);
        }
        if(sum % 3 == 0) res ++;
        System.out.println(res);
        
    }
}

发表于 2025-03-27 19:29:27 回复(0)
性质:一个整数是3的倍数,当且仅当它的各位数字之和是3的倍数
假设数字num的各位数之和为sum,只需删除num中数位和为sum%3的区间
发表于 2025-07-09 21:47:49 回复(0)