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

  简化数组后的代码就是随书配套的代码

 

posted @ 2016-09-02 17:51  PatrickZhou  阅读(183)  评论(0编辑  收藏  举报