読者です 読者をやめる 読者になる 読者になる

SRM625 Div1Easy PalindromePermutations

解法
左右対称に着目して、その場合の数を求めるためにmapで数え上げた文字の数を半分にする。
奇数の文字が2つ以上あったら回文は作れない。一つの文字のみが奇数なら、中央に一文字置けば回文になる。後は同じものを含む順列を利用する。

(回文の半分の全パターンの同じものを含む順列) / (回文全体の全パターンの同じものを含む順列)

で答えが求まる。

最後のケースでのオーバーフローに注意。計算過程を long double にして tgammal() するとうまくいく。

反省
Div2のコンテストが終わった後解いてみた。まずN=1の単純なケースの例外処理を忘れて落として、その後通して 75.0pt だった。初めオーバーフローを避けるために、階乗計算の一番大きい数を掛けないで、一度分母で割って残りの階乗を書けてから、最後に一番大きな数を掛ける小細工をしたけど、あまり意味がなかったかも知れない。

using namespace std;
typedef long double ld;
class PalindromePermutations {
public:
  double palindromeProbability(string word) {
    
    int const N = word.size();
    if(N==1) return 1.0;
    map<char, int> chrs;
    for(int i=0; i<N; i++) {
      chrs[word[i]] ++;
    }
    int M = chrs.size();
    if(M>25) return 0.0;
    int oddcnt = 0;
    for(const auto& e : chrs) {
      oddcnt += e.second%2;
    }
    if(oddcnt >= 2) return 0.0;
    
    ld anume = 0;
    ld adeno = 1;
    for(const auto& e : chrs) {
      if(e.second==0) continue;
      adeno *= tgammal(e.second+1);
      anume += e.second;
    }
    ld allpat = tgammal(anume+1)/adeno;
    for(auto& e : chrs) {
      e.second /= 2;
    }
    ld nume = 0;
    ld deno = 1;
    for(const auto& e : chrs) {
      if(e.second == 0) continue;
      deno *= tgammal(e.second+1);
      nume += e.second;
    }
    return tgammal(nume+1)/deno/allpat;
  }
};