SRM 509 DIV1 Middle - PalindromizationDiv1

問題


ある文字列が与えられ、それを回文にしたい。

回文にするために複数のアルファベットの追加、削除、変換式が与えられ、各変換式について必要なコストも与えられる。

このとき、回文にするために必要な最小のコストを求める。回文にできないときはー1を返す。

解き方


Editorialを読みました。
通常の回文の解き方をベースに、それを応用できるか。

通常の回文の解き方は以下の通り。

左端と右端の場所が一致、もしくは残り1文字になれば終了。
そうでなければいずれかの操作を行う。
1)回分の左端と右端(a,b-1)のアルファベットがことなれば一致するように変換し、
  変換コストを足して(a+1,b-2)を調べる。
2)左端を削除し、削除コストを足して(a+1,b-1)を調べる。
3)右端を削除し、削除コストを足して(a,b-2)を調べる。

今回はコストの考え方について応用する。
削除については、アルファベットを空白に変換と考えれば変換と同様に操作できる。
同様に追加についても、空白をアルファベットに変換と考えることができる。

上記の操作によって、変換のコストだけをコードに落とせればよい。
変換のコストは問題で与えられるので、配列に保存してからワーシャルフロイドを回して任意の2つのアルファベットの最小の変換コストを求める。

ただし今回は、変換できればよいことから
あるアルファベットa→c とb→cのコストの和が最小であれば上記に置き換える必要がある。

また、例外の判定については定義したINFの和が発生しうるため、
オーバーフローしないように値の判定と更新を行うことに注意。

コード


using namespace std;

#define all(c) (c).begin(),(c).end()
#define FORE(i,d,e) for(int i=d;i<e;i++)
#define FOR(i,s,e) for (int i = int(s); i != int(e); i++)
#define FORIT(i,c) for (typeof((c).begin()) i = (c).begin(); i != (c).end(); i++)
#define ISEQ(c) (c).begin(), (c).end()

const int INF=1000000000;
string word;
int d[27][27],m[27][27],dp[51][51];

class PalindromizationDiv1 {

public:

int rec(int a,int b){
if(dp[a][b]!=-1)return dp[a][b];
if(a==b||a==b-1)return 0;

int ret=INF;
int c1=word[a]-'a',c2=word[b-1]-'a';

int cost1=rec(a+1,b-1);
int cost2=m[c1][c2];
if(ret-cost1>cost2)ret=cost1+cost2;

cost1=rec(a+1,b);
cost2=m[26][c1];
if(ret-cost1>cost2)ret=cost1+cost2;

cost1=rec(a,b-1);
cost2=m[26][c2];
if(ret-cost1>cost2)ret=cost1+cost2;

return dp[a][b]=ret;
}

int getMinimumCost(string word_, vector<string> operations) {
word=word_;
FORE(i,0,27)FORE(j,0,27)d[i][j]= i==j ? 0 : INF;
FORE(i,0,27)FORE(j,0,27)m[i][j]=INF;
memset(dp,-1,sizeof(dp));

FORE(i,0,operations.size()){
string str;
char c1,c2;
stringstream out(operations[i]);
out>>str>>c1;
if(str=="erase"){
out>>d[c1-'a'][26];
}
else if(str=="add"){
out>>d[26][c1-'a'];
}
else{
out>>c2;
out>>d[c1-'a'][c2-'a'];
}
}

FORE(k,0,27)FORE(i,0,27)FORE(j,0,27)if(d[i][k]!=INF&&d[k][j]!=INF)d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
FORE(i,0,27)FORE(j,0,27)FORE(k,0,27)if(d[i][k]!=INF&&d[j][k]!=INF)m[i][j]=min(m[i][j],d[i][k]+d[j][k]);

int ret=rec(0,word.size());

return ret>=INF ? -1 : ret;
}

};

Share this

Related Posts

Previous
Next Post »