C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で総和がKに等しい部分配列の個数を求める方法

整数型の配列 nums と整数 k が与えられたとき、要素の総和がちょうど k に等しくなる「連続する部分配列(サブアレイ)」の総数を求めることを考えます。たとえば nums = [1, 1, 1]、k = 2 の場合、条件を満たすのは [1, 1](インデックス 0〜1)と [1, 1](インデックス 1〜2)の 2 つなので、答えは 2 となります。

解法の考え方:累積和とハッシュマップ

すべての部分配列を総当たりで調べる方法もありますが、O(n²) の計算量がかかり非効率です。そこで役立つのが「累積和(プレフィックスサム)」のテクニックです。ある位置で終わる部分配列の和が k になるのは、その位置までの累積和から、より手前の位置までの累積和を引いた差が k に等しいときです。つまり「現在の累積和 − k」が過去に何回現れたかを数えれば、それがそのまま答えになります。この出現回数をハッシュマップで管理することで、配列を一度走査するだけで解けます。

具体的な手順は以下の通りです。

  • マップ sums を定義し、temp := 0、sums[0] := 1、ans := 0 として初期化する
  • i を 0 から配列サイズ − 1 までループする
    • temp := temp + n[i] として累積和を更新する
    • sums にキー (k − temp) が存在する場合は、ans := ans + sums[k − temp] を行う
    • sums[−temp] の値を 1 増やす
  • 最後に ans を返す

初期化で sums[0] = 1 としているのは、配列の先頭から始まる部分配列も正しくカウントできるようにするためです。また、この実装ではマップのキーに累積和の符号を反転した値(−temp)を登録しているため、探索も k − temp という形になっています。よく見られる「sums[temp − k] を参照する」標準的な書き方と、結果は完全に同一です。

例(C++コード)

理解を深めるために、実際の実装を見てみましょう。

#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
   int subarraySum(vector<int>& n, int k) {
      unordered_map <int, int> sums;
      int temp = 0;
      sums[0] = 1;
      int ans = 0;
      for(int i = 0; i < n.size(); i++){
         temp += n[i];
         if(sums.find(k - temp) != sums.end()){
            ans += sums[k - temp];
         }
         sums[-temp]++;
      }
      return ans;
  }
};
main(){
   Solution ob;
   vector<int> v = {1,1,1};
   cout << (ob.subarraySum(v, 2));
}

入力

[1,1,1]
2

出力

2

計算量

このアルゴリズムは配列を一度だけ走査するため、時間計算量は O(n) です。累積和の出現回数を最大で n 種類保持するため、空間計算量も O(n) となります。二重ループですべての部分配列の和を確認する素朴な O(n²) のアプローチと比べて、大きな入力でも高速に動作します。

  1. C++のプレフィックス和(累積和)を活用してO(n)で最大部分配列和を求める方法

    問題概要 正の整数と負の整数が混在する配列が与えられたとき、その配列の中で合計値が最大となる部分配列(連続した要素の並び)の合計を求める問題です。 例 入力配列が {-12, -5, 4, -1, -7, 1, 8, -3} の場合、合計が最大になる部分配列は {1, 8} となるため、出力は 9 になります。 アルゴリズム この問題は、プレフィックス和(累積和)を利用することで O(n) の時間計算量で効率的に解くことができます。考え方の核心は、「ある位置 i で終わる部分配列の合計の最大値」は「prefix_sum[i] から、それ以前に現れた最小の累積和を引いた値」で表せるという点です

  2. C++で厳密に増加する部分配列の最大和を求めるアルゴリズム

    問題の概要n 個の整数からなる配列が与えられたとき、その中に存在する「厳密に増加する(strictly increasing)部分配列」の中で、要素の合計が最大となるものを求めます。例として、次のような配列を考えてみましょう。[1, 2, 3, 2, 5, 1, 7]この配列には、厳密に増加している部分配列が3つ存在します。{1, 2, 3}{2, 5}{1, 7}それぞれの合計は 6、7、8 となり、この中で最大となるのは {1, 7} の合計 8 です。解き方の考え方この問題は、現在の部分配列の合計(current_sum)とこれまでの最大合計(max_sum)を追跡しながら配列を一度だけ