kmjp's blog

競技プログラミング参加記です

TopCoder SRM 617 Div1 Hard FarStrings

800ptとはいえこれは自力では解けなかったかな…。
http://community.topcoder.com/stat?c=problem_statement&pm=12526

問題

文字列SとTの距離は、Sに以下の処理を行ってTにするのに必要な最小処理回数である。

  • 任意の1文字を任意の位置に挿入する
  • 任意の1文字を削除する
  • 任意の1文字を別の文字に変える

文字列Tが与えられる。
文字列の長さをNとしたとき、Tとの距離が1,2,3...NとなるN文字の文字列のうち、それぞれ辞書順最小のものを答えよ。

解法

自力で解けなかったので他の回答を見て練習。

求める文字列、すなわちTとの距離がiの文字列をU[i]とする。
U[i]の1文字目からa, b, c...と埋めていき、U[i]がそのようなprefixを持つとき、U[i]とTの距離の最小値と最大値を求める。
iが最大値と最小値の間に来るような文字があれば、それを採用していく。

問題はU[i]をprefixとする文字列とTの距離。
これはLongest Common Stringの要領でDPしていき、最小値を求めていく。
その際、Tの文字が完了する前にU[i]のprefixとマッチし終わった場合、残りの文字をTの残りと合わせるか合わせないかで距離の最小・最大値の範囲を求められる。

N<=25なため、必ずTのどれともマッチしないアルファベットが存在するので、最大値を取ることは常に可能。

class FarStrings {
	public:
	
	pair<int,int> dist(string A,string B,int bl) {
		int al=A.size();
		int dp[51][51],dp2[51][51],x,y;
		FOR(x,51) FOR(y,51) dp[x][y]=dp2[x][y]=10000;
		dp[0][0]=dp2[0][0]=0;
		
		FOR(x,al+1) FOR(y,al+1) {
			if(x<al) dp[x+1][y]=min(dp[x+1][y],dp[x][y]+1), dp2[x+1][y]=min(dp2[x+1][y],dp2[x][y]+1);
			if(y<al) dp[x][y+1]=min(dp[x][y+1],dp[x][y]+1), dp2[x][y+1]=min(dp2[x][y+1],dp2[x][y]+1);
			if(x<al) {
				if(y<bl) {
					dp[x+1][y+1]=min(dp[x+1][y+1],dp[x][y]+(A[x]!=B[y]));
					dp2[x+1][y+1]=min(dp2[x+1][y+1],dp2[x][y]+(A[x]!=B[y]));
				}
				else if(y<al) {
					dp[x+1][y+1]=min(dp[x+1][y+1],dp[x][y]);
					dp2[x+1][y+1]=min(dp2[x+1][y+1],dp2[x][y]+1);
				}
			}
		}
		
		return pair<int,int>(make_pair(dp[al][al],dp2[al][al]));
	}
	
	vector <string> find(string t) {
		int ss=t.size(),i,j;
		vector<string> S;
		
		FOR(i,ss) {
			string h=string(ss,'*');
			FOR(j,ss) {
				for(h[j]='a';h[j]<='z';h[j]++) {
					pair<int,int> p=dist(t,h,j+1);
					if(p.first<=i+1 && i+1<=p.second) break;
				}
			}
			S.push_back(h);
		}
		return S;
	}
};

まとめ

prefixマッチ完了後の最大・最小値の処理は自分では思い浮かばなかったわ…。