zoukankan      html  css  js  c++  java
  • HackerRank

    Classic and challenging DP! And you need combine several tricks together with DP to make it 100% pass.

    My main reference is here: https://github.com/havelessbemore/hackerrank/blob/master/algorithms/bit_manipulation/string-transmission.java

    Basically it is a counting problem. It is easy to figure out that the possible count should be SUM(Cr(n, 0), Cr(n, 1),.. Cr(n, k)) without the limitation of "no periodic strings". And then we can count how many "periodic" strings there are. DP is useful here - two states: length of current substr, and number of corrupted bits -> DP[ending index][no. of corrupted bits]. We can go index by index. Grab this fact in your mind: periodic strings <=> all char[i + divisor * t] are identical, so in case there are different digits in [i, i + d, i + 2d ..], we can either flip 0s or 1s. Each flipping will increase the 2nd state: "number of corrupted bits". Since indexes are independent with each other, we can simply use "+" to advance in the DP.

    Optimization: dp[len + 1] only depends on dp[len], we can use rolling-array. Oh another one, to get Cr(n, k), Pascal's Triangle is a good choice here.

    #include <iostream>
    #include <string>
    #include <vector>
    #include <unordered_map>
    using namespace std;
    
    #define MAX_LEN 1001
    
    #define MOD 1000000007
    inline int mod(int num)
    {
        return (num % MOD + MOD) % MOD;
    }
    
    int main()
    {
        //    Pascal's Triangle to compute Cr(n, k)
        vector<vector<int>> cr(MAX_LEN, vector<int>(MAX_LEN, 0));
        cr[0][0] = 1;
        for(int i = 1; i < MAX_LEN; i ++)
        {
            cr[i][0] = 1;
            for (int j = 1; j <= i; j ++)
                cr[i][j] = mod(cr[i - 1][j - 1] + cr[i - 1][j]);
        }
        
        //
        int t; cin >> t;
        while (t--)
        {
            int n, k; cin >> n >> k;
            string str; cin >> str;
            int len = str.length();
    
            //    Original total: SUM(Cr(n, 0), Cr(n, 1), Cr(n, 2),.. Cr(n, k))
            int ret = 0;
            for (int i = 0; i <= k; i ++)
                ret = mod(ret + cr[len][i]);
    
            //    Get all divisors
            vector<int> divisor;
            for (int d = 1; d < len; d ++)
                if (len % d == 0)    divisor.push_back(d);
    
            bool bSelfRepeated = false;
    
            //    DP
            vector<vector<int>> dp(divisor.size(), vector<int>(MAX_LEN + k, 0));
    
            //    For each divisor..
            for (int id = 0; id < divisor.size(); id ++)
            {
                int d = divisor[id];
    
                /*
                 *    Original DP:
                 *    DP[substr_ending_index][corrupted bits no.] = no. of repeated string, where
                 *        DP[len + 1][cnt + zeroCnt] += DP[len][cnt]                    "For flipping 0s"
                 *        DP[len + 1][cnt + len / divisor - zeroCnt] += DP[len][cnt]    "For flipping 1s"
                 *
                 *    Idea is like this: for a repeated string [pattern]+, all terms above are for pattern only
                    len is the ending substr index into pattern. We check index by index within this pattern.
                    For each divisor-th sequence, (i, i + d, i + 2d..), we can simply flip the zeros in it to make 
                    chars at (i, i + d, i + 2d..) identical to all 1, and so does for flipping 1s (2nd equation above).
    
                    Now, let's check DP state at index (i + 1). Say at index i (i + 1, i + 1 + d, i + 1 + 2d..), number 
                    of zeros is zeroCnt, then the cnt of dp[i + 1] can be built upon dp[i], for each of (0..k) of course
                    Since each index is independent with each other, it is simply "+" upon the previous count.
                 *
                 *    If you want to get 100% pass, you have to apply rolling array optimization, as below
                 */        
    
                //    DP Start            
                dp[id][0] = 1;
                            
                for (int j = 0; j < d; j ++)
                {
                    //    Counting ZEROs
                    int zeroCnt = 0;
                    for (int i = j; i < len; i += d)
                        if(str[i] == '0') zeroCnt ++;
    
                    vector<int> pre = dp[id];
                    std::fill(dp[id].begin(), dp[id].end(), 0);
    
                    for (int i = 0; i <= k; i ++)
                    {
                        if (pre[i] > 0)
                        {
                            dp[id][i + zeroCnt]         = mod(dp[id][i + zeroCnt]         + pre[i]);    // for flipping 0s
                            dp[id][i + len/d - zeroCnt] = mod(dp[id][i + len/d - zeroCnt] + pre[i]);    // for flipping 1s
                        }
                    }
                }
    
                if (dp[id][0] > 0) bSelfRepeated = true;
    
                //    Avoid duplicated counting
                for (int pid = 0; pid < id; pid ++)
                    if(d % divisor[pid] == 0)
                        for(int i = 0; i <= k; i ++)    dp[id][i] = mod(dp[id][i] - dp[pid][i]);
    
                //    Repeated string number counting done.
                //    Now removing no. of repeated pattern strings
                for(int i = 1; i <= k; i ++)
                    ret = mod(ret - dp[id][i]);
            }
    
            //    If input str itself is in repeated pattern..
            if (bSelfRepeated)    ret = mod (ret - 1);
    
            cout << ret << endl;
        }// while(t--)
        return 0;
    }
  • 相关阅读:
    spring boot 配置时区差别
    Java 中序列化与反序列化引发的思考?
    Http 状态码总结
    Springboot
    ConcurrentHashMap 实现缓存类
    C#基础(二)拆箱与装箱,循环与选择结构,枚举
    C#基础知识(一)自己总结的。。。
    贪吃蛇大作战双人版加强版完整代码加详细步骤,不懂问博主 啦啦啦。
    C#数组随机生成四个随机数
    C#添加背景音乐
  • 原文地址:https://www.cnblogs.com/tonix/p/4562425.html
Copyright © 2011-2022 走看看