【问题标题】:possible distinct sub arrays可能的不同子数组
【发布时间】:2020-07-13 11:28:32
【问题描述】:

获取许多可能的不同子数组,使得它们最多包含给定数量的奇数。

示例:

arr = [2,1,2,1,3]
m = 2.

答案:

10

说明:

  • 具有 0 个奇数的不同子数组 = [2]
  • 具有 1 个奇数的不同子数组 = [2,1]、[1]、[2,1,2]、[1,2] 和 [3]
  • 具有 2 个奇数的不同子数组 = [2,1,2,1], [1,2,1], [2,1,3], [1,3]

所以总共有 10 个可能的不同子数组。

约束:

数组大小最多可以有1000个元素,m的范围可以从1到数组大小。

public static int process(List<Integer> arr, int m) {
    Set<String> set = new LinkedHashSet<>();
    for (int i = 0; i < arr.size(); i++) {
        for (int j = i; j < arr.size(); j++) {
            int odd = 0;
            StringBuilder sb = new StringBuilder();
            for (int k1 = i; k1 <= j; k1++) {
                if (arr.get(k1) % 2 != 0) {
                    odd++;
                }
                sb.append(arr.get(k1) + " ");
            }
            if (odd <= m) {
                set.add(sb.toString());
            }

        }
    }
    return set.size();
}

该程序适用于小输入,但由于我有 3 个 for 循环,因此对于大输入会失败。解决这个程序的正确方法是什么?

我已经浏览过这篇文章 - find number of subarrays with specified number of odd integers,但这里的问题并未考虑关于 distinct 子数组的问题。

【问题讨论】:

    标签: java algorithm


    【解决方案1】:

    这是一个 O(n^2) 的方法:

    1. 假设元素存储在大小为 n+1 的数组中,位置为 [1....n]
    2. 让我们相应地修改数组:A[0]=0 和 A[i] = A[i-1] + (A[i]%2==1 ? 1 : 0),所以现在 A[i] 存储范围 [0...i] 中的奇数个数
    3. 通知某些范围内奇数计数[i,j] 现在与A[j]-A[i-1] 相同
    4. 现在只需检查 O(n^2) 中的所有可能范围
    5. 对于不同的子数组,我们可以使用 Set/Hash 数据结构,但我们需要有效地存储子数组
    6. 最有效的似乎是散列,我们可以使用一些基数 b 和模 m 进行多项式散列,因此 H[0] = 0, H[i] = H[i-1] * b + A[i] % m 如果 a 和 m 是大于最大输入值的素数,它应该可以工作

    伪代码:

    int res = 0;
    
    for(int i=1; i<=n; i++)
     A[i] = A[i-1] + (A[i]%2 == 1);
    
    for(int i=1; i<=n; i++)
    for(int j=i; j<=n; j++)
      res+=A[j]-A[i-1] <= m;
    

    【讨论】:

    • 步骤 1 到 4 的伪代码很清楚。但我无法理解如何使用 5 和 6 获得不同的子数组。您能否也分享一下代码?跨度>
    • 同Rabin-Karp算法,不懂的可以到这里学习:cp-algorithms.com/string/rabin-karp.html
    • 还请记住,可能会发生碰撞,因此在这种情况下,您可以使用 base 和 module 的不同值多次执行 rabin karp 算法,这将大大减少碰撞机会跨度>
    【解决方案2】:

    我使用 Trie 为这个问题构建了一个 C++ 解决方案。复杂度为 O(n²),但空间成本更低。

    这个想法是建立一个包含所有可能性的 Trie,然后在 Trie 中执行 BFS,注意不要超过奇数的限制。

    #include <bits/stdc++.h>
    using namespace std;
    #define odd(n) (n % 2 != 0)
    
    struct trie {
        unordered_map<int, trie*> root;
        trie(){ root.clear(); }
        bool contains(int key){ return root.find(key) != root.end();}
        void add(int *arr, int index, int n){
            trie *t = this;
            for(int i = index; i < n; i++){
                if(t->contains(arr[i])){
                    t = t->root[arr[i]];
                }
                else{
                    t->root[arr[i]] = new trie();
                    t = t->root[arr[i]];
                }
            }
        }
        int BFS(int m){
            queue<pair<trie*, int>> q;
            q.push(make_pair(this, 0));
            int ans = 0;
            while(q.size()){
                pair<trie*, int> p = q.front();
                q.pop();
                
                for (auto& it: p.first->root) {
                    if(p.second + odd(it.first) <= m) ans++;
                    else continue;
                    q.push(make_pair(it.second, p.second + odd(it.first)));
                }
            }
            return ans;
        }
    };
    
    trie* t;
    
    int main(){
        t = new trie();
        int arr[] = {2,1,2,1,3};
        int n = 5;
        int m = 2;
        
        for(int i = 0; i < n; i++){
            t->add(arr, i, n);
        }
        
        cout<<t->BFS(m)<<endl;
    
        return 0;
    }
    

    输出:10。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多