CF49E Common ancestor
CF49E Common ancestor
题意
有两个字符串S和T,并提供一些转换方案(每一个方案可以将一个字母转换成两个字母),求S和T是否可以由一个相同的字符串转换出来,如果可以,求最短的字符串,若不行,输出-1。
思路
先不去考虑S和T,先考虑单独去某个字符串是否能够由某一个字母扩展而成。
而在实际扩展的过程中是不断使用转换方案的。
若原字母能够扩展出一段区间,那么必然存在一种方案,使得原字母先扩展得到两个新字母,并有这两个字母接管两个子区间。
对于字符串S和字符串T来说,如果说中间存在由一个相同的字母可以扩展出一段距离的话,那么就不妨尝试将这个字母加入到初始字符串。
定义一个dp[i][j]
代表着说对于S串前i个元素来说和对于T串前j个元素来说最短的初始字符串。
#include <bits/stdc++.h>
#define N 60
using namespace std;
int rec[N][4];
bool ok[2][N][N][30];
int dp[N][N];
void solve() {
char s[N], t[N], op[5];
int n;
cin >> s + 1 >> t + 1;
int lens = strlen(s + 1);
int lent = strlen(t + 1);
cin >> n;
for (int i = 0; i < n; i++) {
cin >> op;
rec[i][0] = op[0] - 'a';
rec[i][1] = op[3] - 'a';
rec[i][2] = op[4] - 'a';
}
for (int i = 1; i <= lens; i++)
ok[0][i][i][ s[i] - 'a' ] = 1;
for (int i = 1; i <= lent; i++)
ok[1][i][i][ t[i] - 'a' ] = 1;
for (int i = 2; i <= lens; i++)
for (int st = 1; st <= lens - i + 1; st++) {
int ed = st + i - 1;
for (int k = st + 1; k <= ed; k++)
for (int c = 0; c < n; c++) {
ok[0][st][ed][ rec[c][0] ] |= (ok[0][st][k - 1][ rec[c][1] ] && ok[0][k][ed][ rec[c][2]]);
}
}
for (int i = 2; i <= lent; i++)
for (int st = 1; st <= lent - i + 1; st++) {
int ed = st + i - 1;
for (int k = st + 1; k <= ed; k++)
for (int c = 0; c < n; c++) {
ok[1][st][ed][ rec[c][0] ] |= (ok[1][st][k - 1][ rec[c][1] ] && ok[1][k][ed][ rec[c][2]]);
}
}
memset(dp, 0x3f, sizeof(dp));
dp[0][0] = 0;
for (int i = 1; i <= lens; i++)
for (int j = 1; j <= lent; j++)
for (int k = 1; k <= i; k++)
for (int l = 1; l <= j; l++)
for (int c = 0; c < 26; c++)
if (ok[0][k][i][c] && ok[1][l][j][c])
dp[i][j] = min(dp[i][j], dp[k - 1][l - 1] + 1);
if (dp[lens][lent] > 100)
cout << -1;
else
cout << dp[lens][lent];
}
int main() {
int T_ = 1;
while (T_--)
solve();
return 0;
}