正题
jzoj 5102
题目大意
给你两个串A,B,字母个数相等,可以把B的一个字符移到前面某个位置,问你最少移多少次可以使A,B相等
解题思路
设fi,jf_{i,j}fi,j为A匹配了i-n,B用了j-n
1.当i,j匹配时fi,j=fi+1,j+1f_{i,j}=f_{i+1,j+1}fi,j=fi+1,j+1
2.也可以把j往前移,那么有fi,j=fi,j+1+1f_{i,j}=f_{i,j+1}+1fi,j=fi,j+1+1
3.如果i-n中AiA_iAi的个数比j-n中AiA_iAi的个数少,那么说明B中有剩余的移出来的AiA_iAi,可以填到i的位置,那么有fi,j=fi+1,jf_{i,j}=f_{i+1,j}fi,j=fi+1,j
DP时记录下从哪个状态转移过来
倒退一遍求出所有移动操作,然后把操作之间的影响加上
代码
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define fs first
#define sn second
#define mp make_pair
#define N 2021
using namespace std;
int T, n, x, y, w, c[N], ans[N][3], ns[N][30], nss[N][30], f[N][N], fr[N][N];
char s[N], ss[N];
vector<int>cl[30];
void add(int x)
{for (; x <= n; x += x&-x)c[x]++;return;
}
int ask(int x)
{int sum = 0;for (; x; x -= x&-x)sum += c[x];return sum;
}
int main()
{scanf("%d", &T);while(T--){scanf("%s%s", ss+1, s+1);n = strlen(s+1);memset(c, 0, sizeof(c));memset(ns, 0, sizeof(ns));memset(nss, 0, sizeof(nss));memset(f, 127/3, sizeof(f));for (int i = n; i > 0; --i){for (int j = 0; j < 26; ++j)ns[i][j] = ns[i + 1][j], nss[i][j] = nss[i + 1][j];ns[i][s[i] - 'a']++;nss[i][ss[i] - 'a']++;}f[n + 1][n + 1] = 0;for (int i = n + 1; i > 0; --i)for (int j = n + 1; j > 0; --j)//DP{if (f[i][j + 1] + 1 < f[i][j])f[i][j] = f[i][j + 1] + 1, fr[i][j] = 1;if (ss[i] == s[j] && i <= n && j <= n)if (f[i + 1][j + 1] < f[i][j])f[i][j] = f[i + 1][j + 1], fr[i][j] = 0;if (nss[i][ss[i] - 'a'] <= ns[j][ss[i] - 'a'] && i <= n && j <= n)if (f[i + 1][j] < f[i][j])f[i][j] = f[i + 1][j], fr[i][j] = 2; }printf("%d\n", f[1][1]);x = y = 1;w = 0;while(x != n + 1 || y != n + 1)//求操作{if (!fr[x][y]){add(x + 1);//树状数组求前面匹配的个数x++;y++;}else if (fr[x][y] == 2){cl[ss[x] - 'a'].push_back(x);x++;}else{ans[++w][0] = y;ans[w][1] = cl[s[y] - 'a'].back();ans[w][2] = ask(ans[w][1]);cl[s[y] - 'a'].pop_back();y++;}}for (int i = 1; i < w; ++i)for (int j = i + 1; j <= w; ++j)if (ans[i][1] <= ans[j][1])//互相影响ans[j][2]++;for (int i = 1; i <= w; ++i)printf("%d %d\n", ans[i][0], ans[i][2] + 1);}return 0;
}