3

http://www.hackerrank.comでオンライン プログラミング コンテストに参加しています。私が取り組んでいる問題の 1 つは、回文でもある文字列のアナグラムの数を見つけることです。問題は解決しましたが、アップロードしようとすると、多くのテスト ケースで失敗します。実際のテスト ケースは私には隠されているため、なぜ失敗するのかはわかりませんが、スケーラビリティの問題である可能性があると思います。今、誰かに既製の解決策を教えてもらいたくありません。なぜなら、それは公平ではないからです。ただ、人々が私のアプローチについてどう思うか興味があります。

Web サイトの問題の説明は次のとおりです。

与えられた単語に回文であるアナグラムがあるかどうかを調べる方法を知った王は、別の課題に直面します。彼は、特定の単語の回文であるアナグラムが複数存在する可能性があることを認識しています。回文である与えられた単語に対して可能なアナグラムの数を彼が見つけるのを手伝ってくれますか?

王の言葉は多い。与えられた単語ごとに、王は回文である文字列のアナグラムの数を見つける必要があります。アナグラムの数は多くなる可能性があるため、キングはアナグラムの数 % (10 9 + 7) を必要とします。

入力形式 :
入力文字列を含む単一行

出力形式:
であるアナグラム文字列の数を含む 1 行。palindrome % (109 + 7)

制約 :

  • 1<=length of string <= 105
  • 文字列の各文字は小文字のアルファベットです。
  • 各テストケースには、回文である少なくとも 1 つのアナグラムがあります。

サンプル入力 01 :

aaabbbb

サンプル出力 01 :

3 

説明 :
回文である与えられた文字列の 3 つの順列は、 abbabba 、 bbaaabb 、および bababab として与えることができます。

サンプル入力 02 :

cdcdcdcdeeeef

サンプル出力 02 :

90

問題の説明で指定されているように、入力文字列は 10^5 まで大きくなる可能性があるため、数の飽和の問題が発生するため、大きな文字列のすべての回文は不可能です。また、モジュロ (10^9 + 7) ベースの答えを出すだけでよいので、底 (10^9 + 7) で数値の対数を取り、すべての計算で底 (10^9) で分数部分の逆対数を取ることを考えました。とにかくモジュロになるので、答えの+ 7)。

私のアルゴリズムは次のとおりです。

  1. 各文字の頻度を格納します (回文の定義により、文字列の後半は最初の文字列と同じであるため、文字列の半分だけを調べる必要があります)
  2. 複数の文字が奇数回出現する場合、回文は不可能
  3. それ以外の場合は、各文字の頻度について、回文数を段階的に計算します (動的プログラミング)

DP の場合、副問題は次のとおりです: previous_count = 1

追加キャラごとにadded number of palindromes = previous_count * (number_of_char_already_seen + 1)/(number of char same as current char)

これが私のコードです:

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <fstream>
using namespace std;
#define MAX_SIZE 100001

void factorial2 (unsigned int num, unsigned int *fact) {
    fact[num]++;
    return;
}

double mylog(double x) {
    double normalizer = 1000000007.0; 
    return log10(x)/log10(normalizer);
}

int main() {
    string in;
    cin >> in;
    if (in.size() == 1) { 
        cout << 1 << endl;
        return 0;
    }
    map<char, int> freq;
    for(int i=0; i<in.size(); ++i) {
        if (freq.find(in.at(i)) == freq.end()) {
            freq[in.at(i)] = 1;
        } else {
            freq[in.at(i)]++;
        }
    }
    map<char, int> ::iterator itr = freq.begin();
    unsigned long long int count = 1;
    bool first = true;
    unsigned long long int normalizer = 1000000007;
    unsigned long long int size = 0;
    unsigned int fact[MAX_SIZE] = {0};
    vector<unsigned int> numerator;
    while (itr != freq.end()) {
        if (first == true) {
            first = false;
        } else {
            for (size_t i=1; i<=itr->second/2; ++i) {
                factorial2(i, fact);
                numerator.push_back(size+i);
            }
        }
        size += itr->second/2;
        ++itr;
    }
    //This loop is to cancel out common factors in numerator and denominator
    for (int i=MAX_SIZE-1; i>1; --i) {
        while (fact[i] != 0) {
            bool not_div = true;
            vector<unsigned int> newNumerator;
            for (size_t j=0; j<numerator.size(); ++j) {
                if (fact[i] && numerator[j]%i == 0) {
                    if (numerator[j]/i > 1)
                        newNumerator.push_back(numerator[j]/i);
                    fact[i]--;  //Do as many cancellations as possible
                    not_div = false;
                } else {
                    newNumerator.push_back(numerator[j]);
                }
            }
            numerator = newNumerator;
            if (not_div) {
                break;
            }
        }
    }
    double countD = 0.0;
    for (size_t i=0; i<numerator.size(); ++i) {
        countD += mylog(double(numerator[i]));
    }
    for (size_t i=2; i <MAX_SIZE; ++i) {
        if (fact[i]) {
            countD -= mylog((pow(double(i), double(fact[i]))));
            fact[i] = 0;
        }
    }
    //Get the fraction part of countD
    countD = countD - floor(countD);
    countD = pow(double(normalizer), countD);
    if (floor(countD + 0.5) > floor(countD)) {
        countD = ceil(countD);
    } else {
        countD = floor(countD);
    }
    count = countD;
    cout << count;
    return 0;
}

今、私はこの問題に多くの時間を費やしてきましたが、私のアプローチに何か問題があるのでしょうか、それともここで何かが欠けているのでしょうか。何か案は?

4

2 に答える 2

5

基本的な式は次のとおりです。

p!/(a!*b!...*z!)

ここで、 は単語のpフロアでlength/2あり、a,b,c..,z は単語内の発生頻度 a,b,c..,z の半分のフロアを受容的に示します。

残された唯一の問題は、これをどのように計算するかです。そんな方はこちらをご覧ください。

于 2013-07-08T15:29:44.773 に答える