TopCoder SRM 416 div2

全体的にひどかった(自分が)。

250点問題

与えられた文字列の中の最頻出文字を辞書順に出力する。

public String listMostCommon(String[] text) {
	int freqs[]=new int['z'-'a'+1];
	int max_freq=0;
	for(String t:text) {
		for(int i=0;i<t.length();i++) {
			Character c=t.charAt(i);
			if(c==' ') continue;
			int idx=c-'a';
			freqs[idx]++;
			if(max_freq < freqs[idx]) max_freq=freqs[idx];;
		}
	}
	String res="";
	if(0 < max_freq) for(int i=0;i<freqs.length;i++) 
		if(freqs[i]==max_freq) res=res+new String(new byte[]{(byte)('a'+i)});
	return res;
}

文字コードから文字に変換する方法がわからなくて必死にぐぐってた。

  • 文字コード用の配列を別に作れば逆変換の方法がわからなくてもいける
  • freqs=new int[(int)'z'+1] とかすれば引き算しなくていい

500点問題

与えられた整数Nから、立ってるビットの数が同じでNより大きい最初の数を返す。

    public int getNextNumber(int N) {
    	System.out.println("N:"+N);
    	final int bits=32;
		int outer=-1;
		for(int i=bits-1;0<=i;i--) {
			if( (N&(1<<i))!=0 ){ outer=i; break; }
		}
    	int idx=-1;
    	for(int i=0;i<bits;i++) {
    		final int cur=1<<i;
    		final int nex=1<<(i+1);
    		if( (N&cur)!=0 && (N&nex)==0 ) { idx=i; break; }
    	}
    	System.out.println("idx:"+idx);
		if(idx<outer) {
			return (N|(1<<(idx+1)))&~(1<<idx);
		} else {
			int ones=0;
			for(int i=0;i<idx;i++) {
				if( (N&(1<<i))!=0 ) ones++;
			}
			System.out.println("ones:"+ones);
			int result=0;
			result|=(1<<(idx+1));
			for(int i=0;i<=idx;i++) {
				if(ones==0) return result;
				result|=(1<<i);
				ones--;
			}
		}
		return -1;
    }

試行錯誤のあとがたいへんみてとれますね、しかもSystem testで落ちたという。
0001のnext numberは0010、0100のnext numberは1000。01100のnext numberは10100じゃなく10001。
冷静に考えたら、ビット列が'01'になっている部分を'10'にして、それより前に存在する'1'を前にきっちり詰めればOK。冷静に考えられませんでした。
ビット操作するのにStringの配列にしたりvector<int>使ったり、みんな工夫してる。すごい人はそのまま「ハッカーのたのしみ」的テクニックで解いちゃうんだろうけど。
頭からnビット1にするのは~((~0)<<n)でOKかな。先頭nビットに立ってる1の数を数えるテクニックもあったような気がする。