美文网首页
Google Kickstart 2019 Round B 题解

Google Kickstart 2019 Round B 题解

作者: jingy_ella | 来源:发表于2019-05-22 19:16 被阅读0次

    比赛题目

    1. Building Palindromes

    Problem:
    有N个编号从1到N的block,每个由字母A到Z组成,对N个blocks进行Q次查询,每次查询给出block编号的左右边界Li和Ri,判断每次能否对该范围的blocks重新排序使其组成回文串。
    Solution:

    1. 最简单直观的想法是对每次查询进行暴力判断,即为每次查询区间新建一个map存储不同字母出现的频次,如果区间长度为奇数,那么出现一次的字母恰好一个,如果区间长度为偶数,那么应该不存在仅出现一次的字母。实现代码如下,注意区间开闭。单次查询O(n),总时间复杂度O(N*Q)。
    int n, q;
    string s;
    bool solve(int x, int y) {
        int len = y - x;
        if (len == 1) 
            return true;
        if (len == 2) 
            return (s[x] == s[x + 1]);
        map<char, int> m;
        for (int j = x; j < y; ++j) {
            m[s[j]]++;
        }
        map<char, int>::iterator it;
        int cnt = 0;
        for (it = m.begin(); it != m.end(); ++it) {
            if ((it->second) & 1) 
                cnt++;
        }
        if (len % 2 == 0) 
            return (cnt == 0);
        else 
            return (cnt == 1);
    }
    
    1. 对上述代码进行优化,我们需要加快每个字母频次的计算,如果我们对整个字母串每个字母的出现频次计算前缀和,这样每次子串的各个字母出现频次查询只需要O(1)的时间。建立所有字母前缀和的时间为O(N * |character set|),每次区间查询用时O(|character set|),总的时间复杂度O((N+Q) * |character set|)。

    对处理区间问题常用的前缀和、差分、树状数组、线段树相关知识的总结参考区间问题

    int n, q;
    string s;
    int a[100002][26];
    
    void preprocess() {
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < 26; ++j) {
                a[i + 1][j] = a[i][j];
            } 
            a[i + 1][s[i] - 'A']++;
        }
    }
    bool solve(int x, int y) {
        int len = y - x;
        int cnt = 0;
        for (int i = 0; i < 26; ++i) {
            if ((a[y][i] - a[x][i]) & 1)
                cnt++;
        }
        if (len % 2 == 0) {
            return (cnt == 0);
        }
        else {
            return (cnt == 1);
        }
    }
    int main() {
        int T;
        ios::sync_with_stdio(false);
        cin >> T;
        for (int t = 1; t <= T; ++t) {
            cin >> n >> q;
            cin >> s;
            preprocess();
            int res = 0;
            for (int i = 0; i < q; ++i) {
                int x, y;
                cin >> x >> y;
                if (solve(x - 1, y))
                    res++;
            }
            cout << "Case #" << t << ": " << res << endl;
        }
    }
    

    根据回文的奇偶数规律上面根据len长度进行返回值的判断也可直接写作

        if (cnt <= 1)
            return true;
        else
            return false;
    

    2. Diverse Subarray

    Problem:
    给定N件饰品,每件饰品的类型为Ai,现在需要选择某个连续区间内的饰品带走,当选择区间[l, r]内的饰品时如果某种类型的饰品的个数多于S个则该类饰品必须全部被丢掉。任意选择l和r,求能够带走的饰品的最大数量。
    Solution:

    1. 最简单直观的想法仍然是对每个区间暴力判断,每个区间新建map记录每类饰品的个数,根据规则计算能带走的饰品的最大数目,更新结果。注意下面代码的优化,在第二轮循环中我们从后向前依次判断,当区间长度小于当前结果数时直接剪枝,对于T <= 100, N <= 1000的test set 1可过。
    int n, s;
    int a[100001];
    
    int func(int x, int y) {
        map<int, int> m;
        for (int i = x; i <= y; i++) {
            m[a[i]]++;
        }
        int res = y - x + 1;
        map<int, int>::iterator it;
        for (it = m.begin(); it != m.end(); it++) {
            if ((it->second) > s) {
                res -= (it->second);
            }
        }
        return res;
    }
    
    int solve() {
        int res = 1;
        for (int i = 0; i < n - 1; i++) {
            for (int j = n - 1; j > 0; j--) {
                if((j - i + 1) < res)
                    break;
                int tmp = func(i, j);
                res = max(res, tmp);
            }
        }
        return res;
    }
    
    1. 设计更优的算法,我们首先固定区间起始点,试图在O(n)时间内计算出当前可能区间的最大可带饰品数,那么我们不能再使用map统计每个类型出现的次数,因为这样丢失了位置信息。我们可以将原始序列转换为基于类型判断的加减差分序列,这样问题转换为求每个固定起始点的转换序列的最大连续和,时间复杂度O(n^2)。
      例如,
      \;\;\; 1 \;\;\;\; 1 \;\;\;\; 4 \;\;\;\; 1 \;\;\;\;\; 4 \;\;\;\;\; 4
      起始点为1 可构造
      +1 +1 +1 -2 +1 -2, 最大连续和为3
      起始点为2 可构造
      \;\;\;\;\;\; +1 +1 +1 +1 -2,最大连续和为4
      如果在-2后仍出现相同类型,转换为+0即可,在上述起始点后移的过程中我们发现只需要将1对应的类型中的-2改为+1, 后续的第一个+0改为-2即可。这样我们对区间起始点遍历O(n)的时间也可以进行优化,只需使用满足两类操作的数据结构:
      支持单点修改,支持区间求和
      因此,我们可以使用树状数组或线段树来实现。

    相关文章

      网友评论

          本文标题:Google Kickstart 2019 Round B 题解

          本文链接:https://www.haomeiwen.com/subject/cgtpzqtx.html