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

【C++】文字のASCII値の合計がkで割り切れる「長さkの部分文字列」の個数を効率的に求める方法

問題の概要

本記事では、文字列と整数 k が与えられたときに、「長さがちょうど k である部分文字列のうち、構成する各文字のASCII値の合計が k で割り切れるもの」の個数を求めるアルゴリズムを解説します。

例として、文字列が "BCGABC"、k = 3 の場合を考えてみましょう。部分文字列 BCG のASCII値の合計は 204(B=66、C=67、G=71)であり、また ABC の合計は 198(A=65、B=66、C=67)です。これらはいずれも k = 3 で割り切れるため、答えは 2 となります。

アプローチ:スライディングウィンドウ法

この問題は、スライディングウィンドウ(sliding window)と呼ばれる手法を用いることで効率的に解けます。手順は以下の通りです。

  1. 最初の長さ k の部分文字列に含まれる文字のASCII値の合計を求めます。
  2. その合計が k で割り切れる場合は、カウントを1増やします。
  3. ウィンドウを1文字分右へずらす際には、ウィンドウの左端から外れる文字のASCII値を合計から引き算し、新しく右端に入ってくる文字のASCII値を足し算します。
  4. スライドのたびに合計が k で割り切れるかどうかを判定し、割り切れればカウントを増やします。

各ステップで合計をゼロから再計算する必要がないため、文字列の長さを n としたとき全体の計算量は O(n) に抑えられます。

C++による実装例

#include <iostream>
using namespace std;

int countKLenSubstr(string str, int k) {
    int len = str.length();
    int sum = 0;
    int count = 0;

    // 最初の長さkの部分文字列のASCII値の合計を求める
    for (int i = 0; i < k; i++)
        sum += str[i];

    if (sum % k == 0)
        count++;

    // ウィンドウをスライドしながら判定
    for (int i = k; i < len; i++) {
        int prev_ascii = str[i - k]; // ウィンドウの左端の文字のASCII値
        sum -= prev_ascii;           // 左端の文字を除外
        sum += str[i];               // 新しく入る文字のASCII値を加算

        if (sum % k == 0)
            count += 1;
    }
    return count;
}

int main() {
    string s = "BCGABC";
    int k = 3;
    cout << "Number of substrings: " << countKLenSubstr(s, k);
}

実行結果

Number of substrings: 2

処理の流れの確認

上記のコードでは、次のような流れで処理が進みます。

  • BCG:合計 204 → 3で割り切れる ✔
  • CGA:合計 203 → 割り切れない ✘
  • GAB:合計 202 → 割り切れない ✘
  • ABC:合計 198 → 3で割り切れる ✔

そのため、条件を満たす部分文字列は BCGABC の2つとなり、出力は 2 になります。

まとめ

部分文字列の合計を毎回一から計算する素朴な方法では計算量が O(n×k) になりますが、スライディングウィンドウ法を使えば「1文字除いて1文字足す」という更新だけで済むため、O(n) まで高速化できます。文字列処理において累積和やウィンドウ操作は頻出のテクニックなので、ぜひ覚えておきましょう。

  1. C++で文字列の部分文字列の総数を求める方法を解説

    この記事では、与えられた文字列から作成できる空でない部分文字列の個数を求める方法について解説します。入力 : string = "moon" 出力 : 10 説明 : 部分文字列は m、o、o、n、mo、oo、on、moo、oon、moon の 10 個です。 入力 : string = "yellow" 出力 : 21解法のアプローチ文字列の長さを n とします。上の例からも分かるように、考えられるすべての部分文字列の個数を求めるには、長さ n、(n-1)、(n-2)、(n-3)、……2、1 の部分文字列の個数を順に加算していく必要があります。部分文

  2. C++で文字のASCII値を取得・表示する方法を解説

    ASCII(American Standard Code for Information Interchange:米国標準情報交換コード)には、0から127までの番号が振られた128種類の文字が定義されています。アルファベット、数字、記号など、さまざまな文字に固有の数値が対応付けられているのが特徴です。 主な文字とそのASCII値の例は以下のとおりです。 文字ASCII値 A65 a97 Z90 z122 $36 &38 ?63 大文字と小文字では異なる値が割り当てられている点にも注目してください。たとえば「A」は65、「a」は97となっており、両者の差は32です。この規