TopCode SRM 336 Div1 Medium LikelyWord

問題

文字列が辞書順に並んだ辞書dictionary[]が与えられる。
k文字からなる単語を一つ、ランダムに生成する。


ただし、dictionaryの要素に一致するような単語は生成されない。
一致しない単語はすべて等確率で生成される。
生成した単語を辞書に入れるとき、辞書の何番目に入る確率が一番高いか、求めよ。


一番確率が高いインデックスが存在しなかったり、複数存在する場合は-1を返せ。

制約条件

辞書の要素≦50
辞書の各単語の長さ≦50
辞書の各単語は英小文字からなる
k≦10

方針

簡単に言えば、dic[i+1]-dic[i]の間隔が最も長いところが最も確率の高いところ。
なので、文字列を数字と見て引き算をすればよい。


辞書の単語がk文字ぴったりか、k文字より長いか、k文字より短いかで境界条件が微妙に変わるのがやっかい。

  • dic[i+1]がk文字ぴったりだったら、その文字は間に入れないので-1
  • dic[i+1]がk文字より短かったら、dic[i+1]には'a'をk文字になるまでつめて、更にその文字列自身は間に入れないので-1
  • dic[i]がk文字より短かったら、dic[i]には'a'をk文字になるまでつめて、その文字列は入れるので+1

ソースコード

string f(string a,string b,int c){
	for(int i=a.size()-1;i>=0;i--){
		int d=(int)a[i]-b[i]-c;
		if(d>=26){
			d-=26;
			c=-1;
		}
		else if(d<0){
			d+=26;
			c=1;
		}
		else c=0;
		a[i]='a'+d;
	}
	if(c)a[0]-=26;
	return a;
}

class LikelyWord {
	public:
	int likely(vector <string> dic, int k) 
	{
		dic.insert(dic.begin(),string(k,'a'));
		dic.pb(string(k,'z'));
		
		int n=dic.size();
		int sml[52]={}, just[52]={};
		rep(i,n){
			if(dic[i].size()==k)just[i]=1;
			if(dic[i].size()<k){
				sml[i]=1;
				while(dic[i].size()<k)dic[i].pb('a');
			}
			dic[i]=dic[i].substr(0,k);
		}
		sml[0]=1; just[n-1]=0;
		string mx;
		int ans=-2;
		
		rep(i,n-1){
			string d=f(dic[i+1],dic[i],just[i+1]+sml[i+1]-sml[i]);
			if(ans==-2||d>mx)ans=i, mx=d;
			else if(d==mx)ans=-1;
		}
		return ans;
	}
};