题目大意:
给出一个只由小写英文字母组成的字符串,每次可以把一个字母调到它前面的一个位置,求与目标串相同的最少步数,并输出一组方案。
吹水:
太菜了,比赛时连部分分也没拿。
这个dp还是比较好理解的,但是也不简单,做着非常有意思。
题解:
首先我们很容易想到,每个字符一定只会移一次,这样才是最优的。
然后最关键的是,假设现在把b串(目标串)移动后符合了a串从后面开始的几位,那我们犯不着把已经匹配好的后面几位打乱,于是我们可以想到倒着dp。
设fi,j表示已经把a串的后面i为匹配好,移动了b串的后j位。
1.fi,j = fi+1,j
含义是去过的j位当中有一个刚好和Ai相同的,直接放到这即可。
那么我们需要判断B的后j位当中是否有多的
2.fi,j = fi+1,j+1
Ai =Bj 时,刚好不用移动。
3.fi,j = fi,j+1 +1
取出Bj,先不用。
初值:
fn+1,i = n+1−i (1 <= i <= n)
f1,1就是答案。
于是最小步数就求出来了。
我们还要求移动方案。
首先找到那些不用动的点:
记录每个状态是由哪个状态推来的,如果fi,j 是由fi+1,j+1推来的,显然Bj和Ai就不会移动。
然后我们直接模拟一遍,从A串(目标串)的左边到右边,对于每一个Ai,如果它是被移过来的,直接在b串里随便找一个没被移过且要移动的,移过来即可,这里可以直接暴力把B串右移,反正都是O(n2)
槽点:
在暴力右移的时候,我竟然顺着Bi = Bi−1移动,于是GG,样例还过了,切记,这种整体移动,一定要倒着移,否则就会覆盖,左移当然就顺着做了。
Code:
#include<cstdio>
#include<cstring>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
using namespace std;
const int Maxn = 2005;
char s[Maxn], s1[Maxn];
int n, m, f[Maxn][Maxn], g[Maxn][Maxn][2], sum[26][Maxn], sum1[26][Maxn];
int bz[Maxn], bz2[Maxn];
int main() {
freopen("chinese.in", "r", stdin);
freopen("chinese.out", "w", stdout);
for(scanf("%d", &m); m; m --) {
memset(f, 30, sizeof(f)); memset(g, 0, sizeof(g));
memset(bz, 0, sizeof(bz)); memset(bz2, 0, sizeof(bz2));
scanf("%s", s + 1); scanf("%s", s1 + 1);
n = strlen(s + 1);
fo(i, 0, 25) sum[i][n + 1] = sum1[i][n + 1] = 0;
fd(i, n, 1) {
fo(j, 0, 25) sum[j][i] = sum[j][i + 1], sum1[j][i] = sum1[j][i + 1];
sum[s[i] - 'a'][i] ++; sum1[s1[i] - 'a'][i] ++;
}
fo(i, 1, n + 1) f[n + 1][i] = n - i + 1, g[n + 1][i][0] = n + 1, g[n + 1][i][1] = i + 1;
fd(i, n, 1) {
fd(j, i, 1) {
if(sum[s[i] - 'a'][i] <= sum1[s[i] - 'a'][j]) {
if(f[i + 1][j] < f[i][j])
f[i][j] = f[i + 1][j], g[i][j][0] = i + 1, g[i][j][1] = j;
}
if(s[i] == s1[j] && f[i + 1][j + 1] < f[i][j])
f[i][j] = f[i + 1][j + 1], g[i][j][0] = i + 1, g[i][j][1] = j + 1;
if(f[i][j + 1] + 1 < f[i][j])
f[i][j] = f[i][j + 1] + 1, g[i][j][0] = i, g[i][j][1] = j + 1;
}
}
printf("%d\n", f[1][1]);
int x = 1, y = 1;
while(!(x >= n + 1 && y >= n + 1)) {
int xx = g[x][y][0], yy = g[x][y][1];
if(x + 1 == xx && y + 1 == yy)
bz[x] = 1, bz2[y] = 1;
x = xx; y = yy;
}
fo(i, 1, n) if(!bz[i]) {
fo(j, i, n) if(!bz2[j] && s[i] == s1[j]) {
fd(k, j, i + 1)
bz2[k] = bz2[k - 1], s1[k] = s1[k - 1];
printf("%d %d\n", j, i);
break;
}
}
}
}