蓝桥之统计子矩阵

在这里插入图片描述
样例说明
满足条件的子矩阵一共有 19 , 包含:

大小为 1×1 的有 10 个。

大小为 1×2 的有 3 个。

大小为1×3 的有 2 个。

大小为 1×4 的有 1 个。

大小为 2×1 的有 3 个。

在这里插入图片描述
前缀和二维数组
在这里插入图片描述

前缀和+暴力搜索

import java.util.*;
public class Main{

    private static int ans=0;

    public static void main(String[] args) {
      Scanner scanner=new Scanner(System.in);
      int N=scanner.nextInt();
      int M=scanner.nextInt();
      int K=scanner.nextInt();
      int[][] a=new int[N+1][M+1];
      int[][]  preSum = new int[N+1][M+1];
      for(int i=1;i<=N;i++){
          for(int j=1;j<=M;j++){
              a[i][j]=scanner.nextInt();
              //二维数组中的各个前缀合
              //
              preSum[i][j] = a[i][j]+preSum[i-1][j]+preSum[i][j-1]-preSum[i-1][j-1];
          }
      }
       //暴力枚举二维数组
        for(int i1=1;i1<=N;i1++){//遍历行
           for(int i2=i1;i2<=N;i2++){
               for(int j1=1;j1<=M;j1++){//遍历列
                   for(int j2=j1;j2<=M;j2++){
                      //枚举各个满足要求的前缀和
                       int z=preSum[i2][j2]-preSum[i2][j1-1]-preSum[i1-1][j2]+preSum[i1-1][j1-1];
                      // System.out.println(z);
                       if(z<=K){
                           ans++;
                       }
                   }
               }
           }
       }
        for (int i = 0; i <=N; i++) {
            for (int j = 0; j <=M; j++) {
                System.out.print(preSum[i][j]+" ");
            }
            System.out.println();
        }
        System.out.println(ans);
    }
}

4个for循环时间复杂度比较高
采用前缀和+滑动窗口
首先对每一列进行前缀和

  for(int i=1;i<=N;i++){
          for(int j=1;j<=M;j++){
              a[i][j]=scanner.nextInt();
              preSum[i][j] = a[i][j]+preSum[i-1][j];
          }
      }

在这里插入图片描述
通过滑动窗口我们可以将4个for循环减少至3个。只需两层for循环遍历行,第三场for循环两个代表列的指针进行滑动窗口。
当遇到不满足条件的时候j+1向右移动列指针。

        for(int i1=1;i1<=N;i1++){
            for(int i2=i1;i2<=N;i2++){
                int sum=0;//一个范围的区间和结束需要重新将sum更新为0
                for(int j1=1,j2=1;j2<=M;j2++){
                    sum+=preSum[i2][j2]-preSum[i1-1][j2];//累加区间和
                    System.out.println(sum);
                    while(sum>K){
                        sum-=preSum[i2][j1]-preSum[i1-1][j1];//不符合条件,减去上一列的区间和(通过左边界的最上层的区间和减去左下边界下一层的区间和就等于上一列的区间和)
                        //System.out.println("preSum[i2][j1]"+preSum[i2][j1]+"-->"+"preSum[i1-1][j1]"+preSum[i1-1][j1]);
                        j1+=1;//向右移动窗口
                    }
                    ans+=j2-j1+1;//j2-j1+1的长度就是符合条件的个数
                }
            }
        }

完整代码:

import java.util.Scanner;
import java.io.*;
// 1:无需package
// 2: 类名必须Main, 不可修改

public class Main {
    private static StreamTokenizer re=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));//快速输入
    private static int nextInt() throws IOException {
        re.nextToken();
        return (int)re.nval;
    }
    public static void main(String[] args) throws IOException{
        //Scanner scan = new Scanner(System.in);
        //在此输入您的代码...

        int N=nextInt();
        int M=nextInt();
        int K=nextInt();
        int[][] a=new int[N+1][M+1];
        int[][]  preSum = new int[N+1][M+1];
        for(int i=1;i<=N;i++){
            for(int j=1;j<=M;j++){
                a[i][j]=nextInt();
                preSum[i][j] = a[i][j]+preSum[i-1][j];
            }
        }
        int ans=0;
        for(int i1=1;i1<=N;i1++){
            for(int i2=i1;i2<=N;i2++){
                int sum=0;//一个范围的区间和结束需要重新将sum更新为0
                for(int j1=1,j2=1;j2<=M;j2++){
                    sum+=preSum[i2][j2]-preSum[i1-1][j2];//累加区间和
                //    System.out.println(sum);
                    while(sum>K){
                        sum-=preSum[i2][j1]-preSum[i1-1][j1];//不符合条件,减去上一列的区间和(通过左边界的最上层的区间和减去左下边界下一层的区间和就等于上一列的区间和)
                        //System.out.println("preSum[i2][j1]"+preSum[i2][j1]+"-->"+"preSum[i1-1][j1]"+preSum[i1-1][j1]);
                        j1+=1;//向右移动窗口
                    }
                    ans+=j2-j1+1;//j2-j1+1的长度就是符合条件的个数
                }
            }
        }
        System.out.println(ans);
    }
}

模拟过程
在这里插入图片描述

### 蓝桥杯子串统计问题解析 #### 计算字符贡献值的方法 对于字符串中每个字符的贡献值计算方法如下:所谓贡献值是指该字符能影响到的子串数量。例如,在字符串`bacbeb`中,位于索引3处的字符'b'可以形成多种子串,具体来说有`acbe`, `acb`, `cbe`, `cb`, `b`, 和`be`共六个子串[^1]。 为了更高效地求解这个问题,可以通过数学方式来确定某个特定位置上的字符所能构成的有效子串数目。假设当前考察的是第i个字符,则此字符左侧最近一次出现的位置记作left(i),右侧下一次出现的位置设为right(i)。那么,基于这两个边界条件,可得该字符参与形成的合法子串总数目=(i-left(i))*(right(i)-i)。 这种方法的时间复杂度仅为O(N),其中N代表字符串总长度,因此非常适合处理较长的数据集。 #### 实现代码示例 下面给出了一段Python实现上述逻辑的简单例子: ```python def calculate_contribution(s): from collections import defaultdict last_occurrence = defaultdict(lambda : -1) next_occurrence = [len(s)] * len(s) stack = [] result = 0 for index, char in enumerate(s+"#"): while stack and s[stack[-1]] != char: pos = stack.pop() left_bound = last_occurrence[char]+1 if char in last_occurrence else 0 right_bound = index contribution = (pos - left_bound + 1)*(right_bound - pos) result += ord(char)*contribution if not stack or s[stack[-1]] == char: last_occurrence[char] = max(last_occurrence[char],index-1) stack.append(index) return result % ((10 ** 9)+7) print(calculate_contribution("ababc")) # 应输出28 ``` 这段代码实现了对给定字符串`s="ababc"`按照题目要求进行运算并返回最终的结果28。注意这里采用了模数取余操作以防止数值过大溢出。 #### 处理周期性子串检测 当涉及到寻找具有固定模式重复出现的最小子串时,如在另一个案例中提到的情况——判断一个字符串是否存在某种规律性的重复结构(即所谓的“周期”),则需采用不同策略来进行分析。这类问题通常会利用KMP算法或其他相似的技术手段去识别潜在的循环节,并据此推断整个序列可能存在的最小重复单元大小[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值