Codeforces 156 A. Message

問題

英小文字からなる文字列sが与えられる。sの連続する部分文字列uを任意に一つ選ぶ。
uに対して、

  • 先頭または末尾に一字を加える。
  • 先頭または末尾の一字を消す。
  • 任意の一文字を、別の文字に変える。

の操作を行うことができる。


最小で何回の操作を行えば、tを作ることができるか、求めよ。

制約条件

sの長さ≦2000
tの長さ≦2000

方針

uの文字を消すという操作は行わなくていい。
(そんなことをするくらいなら、最初から短い文字列を選んでおけばいいから)


なので、先頭、末尾に一字加えるか、一字を別の文字に変える操作のみを考えればいい。
dp[i][j][k]を、sをi文字目まで使って、tをj文字目まで使って、
k=0のときはuがまだ始まっていなくて(先頭に文字を加えることは可能)、
k=1のときはuが始まっていて(文字を変更することのみ可能)、
k=2のときはuがおわりかけ(末尾に文字を加えることが可能)
であるようなときの、コストの最小値とする。


これを更新していくような動的計画法をすればいい。

ソースコード

int dp[2001][2001][3];

int main(){
	string a, b;
	cin>>a>>b;
	int n = a.size(), m = b.size();
	rep(i,n+1)rep(j,m+1)rep(k,3)dp[i][j][k] = inf;
	rep(i,n+1)dp[i][0][0] = 0;
	rep(i,n+1)rep(j,m+1)rep(k,3)if(dp[i][j][k] != inf){
		if(k==0&&j<m)dp[i][j+1][0] = min(dp[i][j+1][0], dp[i][j][k] + 1);
		if(j<m)dp[i][j+1][2] = min(dp[i][j+1][2], dp[i][j][k] + 1);
		if(k<2&&i<n&&j<m)dp[i+1][j+1][1] = min(dp[i+1][j+1][1], dp[i][j][k] + (a[i] != b[j]));
	}
	int ans = inf;
	rep(i,n+1)rep(k,3)ans = min(ans, dp[i][m][k]);
	cout<<ans<<endl;
	return 0;
}