POJ-2945: Find the Clones
keyword
ハッシュ C++
問題概要
長さM(<20)のA,T,G,Cからなる文字列がN(<20000)個与えられる。個数の分布を求める問題。
解法
テストケースが多いらしく、TLEと戦わなければいけない。mapに突っ込んでいけばO(M*N*log N)で解けるけど、mapは定数が重いので落ちる。vectorに突っ込んでソートする方法でも(vectorをreserveしたにも関わらず)落ちた。これはソートするときにMがかかるのがいけないので、文字列を整数にencodeしてやる。ここで32bitのハッシュを使ったら無事通ったのだけど、よく考えると32bitじゃsqrt(2^32)=2^16なので衝突の可能性が割とあった。では64bitにすればよいかと言うと、そもそも文字列の種類が4^20しかないのでそれもうハッシュ値使う意味がない。最初から4進数だと思ってencodeしてやればよかった。計算量O(N*(M+log N))。ちなみにTrie使ったら実際に早くなるかどうかはさておき計算量は落とせる。実装時間14分。
#include <cstdio> #include <vector> #include <string.h> #include <string> #include <algorithm> using namespace std; const int MAX_N = 20009; const int MAX_M = 23; const unsigned int P = 1000000007; int N, M; int ans[MAX_N]; unsigned int ps[MAX_M]; inline unsigned int hash_func(char buf[]){ unsigned int ret = 0; for(int i=0; i<M; i++){ ret += buf[i] * ps[i]; } return ret; } void proc(){ char buf[30]; vector<unsigned int> ws; ws.reserve(N+1); for(int i=0; i<N; i++){ scanf("%s ",buf); ws.push_back(hash_func(buf)); } sort(ws.begin(), ws.end()); ws.push_back(0); memset(ans, 0, sizeof(ans)); int cur = 0; for(int i=0; i<N; i++){ if(ws[i] != ws[i+1]){ ans[cur]++; cur = 0; } else{ cur++; } } for(int i=0; i<N; i++){ printf("%d\n",ans[i]); } } int main(){ ps[0] = 1; for(int i=1; i<MAX_M; i++){ ps[i] = ps[i-1] * P; } while(scanf("%d%d ", &N, &M), (N||M)){ proc(); } return 0; }