9-8
题目讲解中的“如果记录每个颜色的第一次出现位置”指的是出现在最终序列中的位置。
如题目讲解中所说,在计算过程中并不关心每个颜色的L(c),关心的是所有L(c)的sum值。
设题目的最终答案是ans,d[i][j]表示两个序列分别移走了i和j个元素之后,此时合并的序列对ans的贡献值,
设序列1的前一个字符构成序列1i,序列2的前j个字符构成序列2j,
d[i][j]并不是序列1i和2ji合并后L(c)的sum值,因为在计算d[i][j]的公式中,c[i-1][j]或c[i][j-1]的值是根据sp[],ep[],sq[],eq[]计算的,而这几个数组是对原始的两个序列分析而得到的,
并不是对序列1i和2j分析而得到的。
d[i][j] = min{d[i-1][j] + c[i-1][j], d[i][j-1] + c[i][j-1]}
题目的答案 ans = d[n][m]。
d[0][0] == 0
c[i][j] 表示两个序列分别移走了i和j个元素之后,此时合并的序列中有多少个已经开始但尚未结束的字母。
c[0][0] == 0
代码中用两层for循环分别遍历两个序列
计算d[i][j]时,使用公式 d[i][j] = min{d[i-1][j] + c[i-1][j], d[i][j-1] + c[i][j-1]}
计算c[i][j]时,如果 i == 0 ,按照j计算,i != 0,按照i计算
if (i) { c[i][j] = c[i - 1][j]; if (sp[p[i]] == i && sq[p[i]] > j) c[i][j]++; if (ep[p[i]] == i && eq[p[i]] <= j) c[i][j]--; } else if (j) { c[i][j] = c[i][j - 1]; if (sq[q[j]] == j && sp[q[j]] > i) c[i][j]++; if (eq[q[j]] == j && ep[q[j]] <= i) c[i][j]--; }
// 如果p[i]在q[j]根本没有,sq[[i]] == INF > j,eq[p[i]] == 0 <= j
// sp数组初始值为INF,ep数组初始值为0
在上面这段代码中交换i和j也可以
if (j) { c[i][j] = c[i][j - 1]; if (sq[q[j]] == j && sp[q[j]] > i) c[i][j]++; if (eq[q[j]] == j && ep[q[j]] <= i) c[i][j]--; } else if(i) { c[i][j] = c[i - 1][j]; if (sp[p[i]] == i && sq[p[i]] > j) c[i][j]++; if (ep[p[i]] == i && eq[p[i]] <= j) c[i][j]--; }
Accept代码
#define _CRT_SECURE_NO_WARNINGS #include <cstdio> #include <cstring> #include <algorithm> #include <numeric> using namespace std; const int maxn = 5000 + 5; const int INF = 1000000000; char p[maxn], q[maxn]; // starts from index 1 int sp[26], sq[26], ep[26], eq[26]; // start and end indexes for each color //int d[2][maxn], c[2][maxn]; // c[i][j]: how many "incomplete" colors in the mixed sequence int d[maxn][maxn], c[maxn][maxn]; int main() { int T; scanf("%d", &T); while (T--) { scanf("%s%s", p + 1, q + 1); // translate chars 'A' - 'Z' to numbers 0 - 25 int n = strlen(p + 1); for (int i = 1; i <= n; i++) p[i] -= 'A'; int m = strlen(q + 1); for (int i = 1; i <= m; i++) q[i] -= 'A'; for (int i = 0; i < 26; i++) { sp[i] = sq[i] = INF; // initial value for min() ep[i] = eq[i] = 0; } for (int i = 1; i <= n; i++) { sp[p[i]] = min(sp[p[i]], i); ep[p[i]] = i; } for (int i = 1; i <= m; i++) { sq[q[i]] = min(sq[q[i]], i); eq[q[i]] = i; } // dp int t = 0; /* for (int i = 0; i < maxn; i++){ for (int j = 0; j < maxn; j++){ d[i][j] = c[i][j] = 0; } } */ //memset(c, 0, sizeof(c)); //memset(d, 0, sizeof(d)); // 如果用for循环或者memset初始化d[maxn][maxn]和c[maxn][maxn] 会超时 d[0][0] = c[0][0] = 0; /* for (int i = 0; i <= n; i++){ for (int j = 0; j <= m; j++){ if (!i && !j) continue; // calculate d int v1 = INF, v2 = INF; if (i) v1 = d[t ^ 1][j] + c[t ^ 1][j]; // remove from p if (j) v2 = d[t][j - 1] + c[t][j - 1]; // remove from q d[t][j] = min(v1, v2); // calculate c if (i) { c[t][j] = c[t ^ 1][j]; if (sp[p[i]] == i && sq[p[i]] > j) c[t][j]++; if (ep[p[i]] == i && eq[p[i]] <= j) c[t][j]--; } else if (j) { c[t][j] = c[t][j - 1]; if (sq[q[j]] == j && sp[q[j]] > i) c[t][j]++; if (eq[q[j]] == j && ep[q[j]] <= i) c[t][j]--; } } t ^= 1; } printf("%d\n", d[t ^ 1][m]); */ for (int i = 0; i <= n; i++){ for (int j = 0; j <= m; j++){ if (!i && !j) continue; // calculate d int v1 = INF, v2 = INF; if (i) v1 = d[i - 1][j] + c[i - 1][j]; // remove from p if (j) v2 = d[i][j - 1] + c[i][j - 1]; // remove from q d[i][j] = min(v1, v2); if (i) { c[i][j] = c[i - 1][j]; if (sp[p[i]] == i && sq[p[i]] > j) c[i][j]++; if (ep[p[i]] == i && eq[p[i]] <= j) c[i][j]--; } else if (j) { c[i][j] = c[i][j - 1]; if (sq[q[j]] == j && sp[q[j]] > i) c[i][j]++; if (eq[q[j]] == j && ep[q[j]] <= i) c[i][j]--; } /* if (j) { c[i][j] = c[i][j - 1]; if (sq[q[j]] == j && sp[q[j]] > i) c[i][j]++; if (eq[q[j]] == j && ep[q[j]] <= i) c[i][j]--; } else if (i) { c[i][j] = c[i - 1][j]; if (sp[p[i]] == i && sq[p[i]] > j) c[i][j]++; if (ep[p[i]] == i && eq[p[i]] <= j) c[i][j]--; } */ } } printf("%d\n", d[n][m]); } return 0; }
上面的代码使用两个二维数组int d[maxn][maxn], c[maxn][maxn];
因为在计算每一行的内容时只用到了前面一行的内容,可以简化数组,两行就够了
定义数组int d[2][maxn], c[2][maxn];并定义变量t
在for循环之前,t初始化为0
i == 0时计算第一行,t == 0, 设置 d[0][j] 和 c[0][j]
i == 1时计算第二行,t == 1,t^1 == 0,d[t][j]代表当前行,d[t^1][j]代表前一行
i == 2时计算第二行,t == 0,t^1 == 1,d[t][j]代表当前行,d[t^1][j]代表前一行
依次类推
i == n是计算最后一行,d[t][n]是最终的ans,但计算完成后还会进行一次 t ^= 1的操作,所以最终的ans是d[t^1][n],(t^1)^1 == t
简化数组后的代码就是随书配套的代码