【前缀和】B004_NK_病毒检测(暴力 / 累加)

一、Problem

小明最近在做病毒自动检测,他发现,在某些library 的代码段的二进制表示中,如果包含子串并且恰好有k个1,就有可能有潜在的病毒。library的二进制表示可能很大,并且子串可能很多,人工分析不可能,于是他想写个程序来先算算到底有多少个子串满足条件。如果子串内容相同,但是开始或者结束位置不一样,则被认为是不同的子串。

注:子串一定是连续的。例如"010"有6个子串,分别是 "0, “1”, “0”, “01”, “10”, “010”

输入描述:
第一行是一个整数k,表示子串中有k个1就有可能是病毒。其中 0 <= k <= 1 000 000

第二行是一个字符串,就是library的代码部分的二进制表示。字符串长度 <= 1 000 000。并且字符串中只包含"0"或"1".

输出描述:
输出一个整数,所有满足只包含k个1的子串的个数。

输入例子1:
1
1010

输出例子1:
6

例子说明1:
满足条件的子串有:"1", "1", "10", "01", "10", "010".

二、Solution

这题和 能被 K 整除的子数组 很像…

方法一:dp

定义状态 f [ c n t − k ] f[cnt-k] f[cntk] 表示字符 1 的个数为 c n t − k cnt-k cntk 时,字符 0 的个数,以下面的数据为例

k = 1
0010111

cnt 表示到达当前位置 i i i 时遇到的字符 1 的个数。

  • i = 2 i = 2 i=2 时,此时 cnt = 1,ans 应加上 f [ c n t − 1 ] = 2 f[cnt-1] = 2 f[cnt1]=2(位置 2 的前面的 0 的个数为 2)
  • i = 4 i = 4 i=4 时,此时 cnt = 2,ans 应加上 f [ c n t − 1 ] = f [ 1 ] = 1 f[cnt-1] = f[1] = 1 f[cnt1]=f[1]=1(位置 4 前面的 0 的个数为 1 个)
  • i = 6 i = 6 i=6 时,此时 cnt = 4,ans 应加上 f [ c n t − 1 ] = f [ 3 ] = 0 f[cnt-1] = f[3] = 0 f[cnt1]=f[3]=0(位置 6 前面的 0 的个数为 0 个)

总结:当前位置的状态依赖的是到达当前位置 i i i 时剩余字符 1 的数量为 cnt-k 时字符 0 的数量。

import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
    static class Solution {
        void init() {
            Scanner sc = new Scanner(new BufferedInputStream(System.in));
            int k = sc.nextInt();
            char[] s = sc.next().toCharArray();

            int n = s.length, cnt = 0, f[] = new int[n];
            long ans = 0;
            f[0] = 1;

            for (int i = 0; i < n; i++) {
                if (s[i] == '1')
                    cnt++;
                if (cnt - k >= 0)
                    ans += f[cnt-k];
                f[cnt]++;
            }
            System.out.println(ans);
        }
    }
    public static void main(String[] args) throws IOException {  
        Solution s = new Solution();
        s.init();
    }
}

复杂度分析

  • 时间复杂度: O ( ) O() O()
  • 空间复杂度: O ( ) O() O()

你可能感兴趣的:(#,前缀和与差分)