Mckee Tech Blog

C++で回文数を列挙

競技プログラミング
最終更新日 : 25/07/13

C++で任意の基数に関する回文数を列挙するプログラムを整理しました。これをベースにして様々な判定を加えられると思います。

回文数を生成する方針

回文数の後半はその前半を折り返したような形をしています。したがって前半は任意の数としてループを回していけば上手くいきそうです。ただしただ折り返すだけでは桁数が偶数の回文数しか列挙できないため、桁数が奇数の回文数を作る際には折り返し方に工夫が必要です。

数を折り返す方法についてはこの記事に書いていますので、よろしければ参照してみてください。

プログラム

1から任意の数maxまでの範囲でbase進数での回文数を探索するプログラムになります。計算量はO(√(max) * log(max))のはずです。

#include <iostream>
#include <vector>
using namespace std;


// 基数のデフォルトは10
long long reverse_number(long long n, long long base = 10) {
  long long reversed_n = 0;
  while (n > 0) {
    reversed_n = reversed_n * base + (n % base);
    n /= base;
  }
  return reversed_n;
}

void generate_palindromes(long long max, long long base = 10) {
  // 基数のべき乗を保存する配列
  vector<long long> pow_base = {1};

  // 桁数
  long long number_length = 1;

  while(1){
    // 桁数の追加
    while(pow_base.size()<number_length){
      pow_base.push_back(pow_base.back()*base);
    }

    // 回文数の後半は前半を折り返したものであるから前半部のみを持っておく
    long long half_palindrome = pow_base[(number_length-1)/2];

    // 桁が繰り上がる直前まで回文数を列挙する
    for(;half_palindrome<base*pow_base[(number_length-1)/2];half_palindrome++){
      long long palindrome = 0;

      // 桁数が偶数の回文数は任意の数の末尾にその反転を加えるようにして得られる
      if(number_length%2==0){
        palindrome+=half_palindrome*pow_base[number_length/2];
        palindrome+=reverse_number(half_palindrome,base);
      }
      // 桁数が奇数の回文数は任意の数の末尾に最下位の桁を除いた反転を加えるようにして得られる
      else{
        palindrome+=half_palindrome*pow_base[(number_length-1)/2];
        palindrome+=reverse_number(half_palindrome/base,base);
      }

      if(palindrome>max) {
        return;
      }
      
      cout<<palindrome<<endl;
    }

    // 列挙する回文数の桁数の繰り上げ
    number_length++;
  }
}

int main() {
  // 100までの回文数を列挙
  generate_palindromes(100);

  // 100までの2進数表記での回文数を列挙
  generate_palindromes(100,2);
}

参考

ABC 414 C問題 解説
https://atcoder.jp/contests/abc414/editorial/13452

C++

関連記事