3127.构造相同颜色的正方形

目标

给你一个二维 3 x 3 的矩阵 grid ,每个格子都是一个字符,要么是 'B' ,要么是 'W' 。字符 'W' 表示白色,字符 'B' 表示黑色。

你的任务是改变 至多一个 格子的颜色,使得矩阵中存在一个 2 x 2 颜色完全相同的正方形。

如果可以得到一个相同颜色的 2 x 2 正方形,那么返回 true ,否则返回 false 。

示例 1:

输入:grid = [["B","W","B"],["B","W","W"],["B","W","B"]]
输出:true
解释:
修改 grid[0][2] 的颜色,可以满足要求。

示例 2:

输入:grid = [["B","W","B"],["W","B","W"],["B","W","B"]]
输出:false
解释:
只改变一个格子颜色无法满足要求。

示例 3:

输入:grid = [["B","W","B"],["B","W","W"],["B","W","W"]]
输出:true
解释:
grid 已经包含一个 2 x 2 颜色相同的正方形了。

说明:

  • grid.length == 3
  • grid[i].length == 3
  • grid[i][j] 要么是 'W' ,要么是 'B' 。

思路

有一个 3 x 3 矩阵,每一个格子有黑白两色,分别用 B W 表示。问能否修改至多一个格子的颜色使矩阵中存在一个 2 x 2 的纯色(颜色相同)矩阵。

可能的 2 x 2 矩阵只有4个,它们有一个公共格子,以它为中心向左上、右上、左下、右下方向判断即可。可以分情况讨论,修改颜色的格子是中间的格子,那么要求其余的三个格子颜色相同。否则,其余格子与中间格子颜色不同的个数最多只能有一个。综上,与中心格子不同的颜色数只能为0、1、3,只需判断不等于2即可。

代码

/**
 * @date 2024-08-31 21:28
 */
public class CanMakeSquare3127 {
    static private final int[][][] directions = new int[][][]
    {
            {{-1, 0}, {-1, -1}, {0, -1}},
            {{1, 0}, {1, -1}, {0, -1}},
            {{-1, 0}, {-1, 1}, {0, 1}},
            {{1, 0}, {1, 1}, {0, 1}}
    };

    public boolean canMakeSquare(char[][] grid) {
        char mid = grid[1][1];
        for (int[][] direction : directions) {
            int cnt = 0;
            for (int[] cor : direction) {
                if (mid != grid[1 + cor[0]][1 + cor[1]]) {
                    cnt++;
                }
            }
            if (cnt != 2) {
                return true;
            }
        }
        return false;
    }

}

性能

3153.所有数对中数位不同之和

目标

你有一个数组 nums ,它只包含 正 整数,所有正整数的数位长度都 相同 。

两个整数的 数位不同 指的是两个整数 相同 位置上不同数字的数目。

请你返回 nums 中 所有 整数对里,数位不同之和。

示例 1:

输入:nums = [13,23,12]
输出:4
解释:
计算过程如下:
- 13 和 23 的数位不同为 1 。
- 13 和 12 的数位不同为 1 。
- 23 和 12 的数位不同为 2 。
所以所有整数数对的数位不同之和为 1 + 1 + 2 = 4 。

示例 2:

输入:nums = [10,10,10,10]
输出:0
解释:
数组中所有整数都相同,所以所有整数数对的数位不同之和为 0 。

说明:

  • 2 <= nums.length <= 10^5
  • 1 <= nums[i] < 10^9
  • nums 中的整数都有相同的数位长度。

思路

有一个数组其元素的数位长度相同,针对数组中所有可能的数对组合(即任选两个元素),比较其数位不同的个数并累加求和。

C(n,2) = n(n-1)/2 时间复杂度为 O(n^2),再加上数位比较,暴力枚举会超时。

考虑到所有元素的数位 digitLength 相同,那么可以统计所有元素该位上数字出现次数,时间复杂度为 O(digitLength*n)。然后逐数位比较,即在 0~9 之间组合,组合数等于对应数位数字出现次数的乘积。外层数位循环最大为10(可以忽略掉出现次数为0的),内层 0~9 组合数最多(10*9/2),总循环次数最大450,没有增大数据规模。

代码


/**
 * @date 2024-08-30 9:33
 */
public class SumDigitDifferences3153 {

    public long sumDigitDifferences(int[] nums) {
        int n = nums.length;
        int first = nums[0];
        int digitsLength = 0;
        while (first > 0) {
            digitsLength++;
            first /= 10;
        }
        int[][] cnt = new int[digitsLength][10];
        for (int i = 0; i < n; i++) {
            int d = 0;
            int num = nums[i];
            while (num > 0) {
                cnt[d++][num % 10]++;
                num /= 10;
            }
        }
        long res = 0L;
        for (int[] digit : cnt) {
            for (int i = 0; i < 10; i++) {
                if (digit[i] == 0) {
                    continue;
                }
                for (int j = i + 1; j < 10; j++) {
                    res += (long) digit[i] * digit[j];
                }
            }
        }
        return res;
    }

}

性能

3142.判断矩阵是否满足条件

目标

给你一个大小为 m x n 的二维矩阵 grid 。你需要判断每一个格子 grid[i][j] 是否满足:

  • 如果它下面的格子存在,那么它需要等于它下面的格子,也就是 grid[i][j] == grid[i + 1][j]
  • 如果它右边的格子存在,那么它需要不等于它右边的格子,也就是 grid[i][j] != grid[i][j + 1]

如果 所有 格子都满足以上条件,那么返回 true ,否则返回 false 。

示例 1:

输入:grid = [[1,0,2],[1,0,2]]
输出:true
解释:
网格图中所有格子都符合条件。

示例 2:

输入:grid = [[1,1,1],[0,0,0]]
输出:false
解释:
同一行中的格子值都相等。

示例 3:

输入:grid = [[1],[2],[3]]
输出:false
解释:
同一列中的格子值不相等。

说明:

  • 1 <= n, m <= 10
  • 0 <= grid[i][j] <= 9

思路

判断矩阵是否满足纵向元素都相等,横向相邻元素各不同。

代码


/**
 * @date 2024-08-29 0:07
 */
public class SatisfiesConditions3142 {

    public boolean satisfiesConditions(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        for (int i = 1; i < m; i++) {
            if (grid[i][0] != grid[i - 1][0]) {
                return false;
            }
        }
        for (int j = 1; j < n; j++) {
            if (grid[0][j] == grid[0][j - 1]) {
                return false;
            }
        }
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (grid[i][j] == grid[i][j - 1] || grid[i][j] != grid[i - 1][j]) {
                    return false;
                }
            }
        }
        return true;
    }
}

性能

3144.分割字符频率相等的最少子字符串

目标

给你一个字符串 s ,你需要将它分割成一个或者更多的 平衡 子字符串。比方说,s == "ababcc" 那么 ("abab", "c", "c") ,("ab", "abc", "c") 和 ("ababcc") 都是合法分割,但是 ("a", "bab", "cc") ,("aba", "bc", "c") 和 ("ab", "abcc") 不是,不平衡的子字符串用粗体表示。

请你返回 s 最少 能分割成多少个平衡子字符串。

注意:一个 平衡 字符串指的是字符串中所有字符出现的次数都相同。

示例 1:

输入:s = "fabccddg"
输出:3
解释:
我们可以将 s 分割成 3 个子字符串:("fab, "ccdd", "g") 或者 ("fabc", "cd", "dg") 。

示例 2:

输入:s = "abababaccddb"
输出:2
解释:
我们可以将 s 分割成 2 个子字符串:("abab", "abaccddb") 。

说明:

  • 1 <= s.length <= 1000
  • s 只包含小写英文字母。

提示:

  • Let dp[i] be the minimum number of partitions for the prefix ending at index i + 1.
  • dp[i] can be calculated as the min(dp[j]) over all j such that j < i and word[j+1…i] is valid.

思路

将字符串划分成子字符串,要求子字符串中每个字符的出现次数相同,求划分后子字符串的最少个数。

想了一会没有头绪,暴力求解该如何做?枚举所有可能的划分?分情况讨论,如果划分为两部分有n-1种分发,如果是三部分有C(n-1,2)种,以此类推。这种枚举显然是不可能的。

考虑贪心算法尽可能多的合并?对应示例2,如果前面尽可能多的合并了 ababab 后面的 ac cd db 就要拆开了,并不是最小的。

如何判断给定区间上字符串是平衡的?还是要遍历计数,然后再来一次循环判断个数是否相同。

先将字符串划分为出现次数都是1的子串,然后再进行合并?如何合并?记录 dp[start][end] ?状态如何转移?

看了题解需要使用动态规划,dp[i] 表示 子串 [0 ~ i) 的最小平衡子串的个数。状态转移方程为 for (int j = i; j >= 0; j--) { if (isBlance(word[j, i])) { dp[i+1] = Math.min(dp[j] + 1, dp[i+1])} }

关键点是使用不同字符的种类乘以最大个数直接判断给定区间是否是平衡的,使用循环会超时。

代码


/**
 * @date 2024-08-28 10:29
 */
public class MinimumSubstringsInPartition3144 {

    public int minimumSubstringsInPartition_v1(String s) {
        int n = s.length();
        int[] dp = new int[n + 1];
        Arrays.fill(dp, Integer.MAX_VALUE);
        dp[0] = 0;
        int[] cnt = new int[26];
        for (int i = 0; i < n; i++) {
            Arrays.fill(cnt, 0);
            int k = 0, max = 0;
            for (int j = i; j >= 0; j--) {
                int index = s.charAt(j) - 'a';
                k += ++cnt[index] == 1 ? 1 : 0;
                max = Math.max(cnt[index], max);
                if (k * max == i - j + 1) {
                    dp[i + 1] = Math.min(dp[j] + 1, dp[i + 1]);
                }
            }
        }
        return dp[n];
    }

}

性能

3134.找出唯一性数组的中位数

目标

给你一个整数数组 nums 。数组 nums 的 唯一性数组 是一个按元素从小到大排序的数组,包含了 nums 的所有 非空子数组中 不同元素的个数。

换句话说,这是由所有 0 <= i <= j < nums.length 的 distinct(nums[i..j]) 组成的递增数组。

其中,distinct(nums[i..j]) 表示从下标 i 到下标 j 的子数组中不同元素的数量。

返回 nums 唯一性数组 的 中位数 。

注意,数组的 中位数 定义为有序数组的中间元素。如果有两个中间元素,则取值较小的那个。

示例 1:

输入:nums = [1,2,3]
输出:1
解释:
nums 的唯一性数组为 [distinct(nums[0..0]), distinct(nums[1..1]), distinct(nums[2..2]), distinct(nums[0..1]), distinct(nums[1..2]), distinct(nums[0..2])],即 [1, 1, 1, 2, 2, 3] 。唯一性数组的中位数为 1 ,因此答案是 1 。

示例 2:

输入:nums = [3,4,3,4,5]
输出:2
解释:
nums 的唯一性数组为 [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3] 。唯一性数组的中位数为 2 ,因此答案是 2 。

示例 3:

输入:nums = [4,3,5,4]
输出:2
解释:
nums 的唯一性数组为 [1, 1, 1, 1, 2, 2, 2, 3, 3, 3] 。唯一性数组的中位数为 2 ,因此答案是 2 。

说明:

  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^5

思路

定义数组的唯一性数组为其所有 子数组 中不同元素个数从小到大排序,求唯一性数组的中位数。

长度为 n 的子数组的个数为 n(n+1)/2,唯一性数组的中位数下标为 n(n+1)/4 - 1 是第 (n(n+1)/2 + 1)/2 个元素。

问题的关键在于,如何快速判断数组中不同元素的个数。我们想要这样一个hash表,可以根据 start end 动态调整其中的元素。

一般来说枚举子数组使用的是双层循环,外层枚举起点,内层从起点开始枚举终点直到结尾,当然也可以外层枚举终点,内层枚举0到终点作为起点,时间复杂度为 O(n^2)。这里的问题在于如何保存区间与对应不重复元素个数的对应关系,以及如何计算不重复元素个数。本来 O(n^2) 就会超时,如果针对每个区间再循环判断,就更不行了。这里其实可以模拟变长的滑动窗口,通过修改窗口中加入与移除元素在map中对应的计数,如果计数为0则删除,这样map里的元素个数即为当前窗口内不重复元素个数。但是并没有保存这个状态(区间对应的不同元素个数)。我们可以将 start, end 压缩到一个long型数字中,倒是也可以记录。假如我们有了这个对应关系,我们还需要将它排序然后取中位数。

看了题解使用的是二分+滑动窗口,确实比较绕,我也没有仔细想清楚,这里面有几个关键点:

  • 唯一性数组中元素的取值范围是 1 ~ n,元素递增的步长为1,如果某个子数组比之前的子数组多了2个不同的元素,那么总是可以去掉其中一个使得子数组仅多1个不同元素。
  • 思考 唯一性元素的个数 小于等于 m 的子数组有多少个?找到唯一性元素个数第一次覆盖 (n(n+1)/2 + 1)/2 的 m 就是要找的答案。
  • 假设我们已经知道 m 对应的 cnt,只需要找到第一个大于等于 (n(n+1)/2 + 1)/2 的cnt即可,可以使用二分查找。
  • 问题转化为 计算唯一性元素的个数 小于等于 m 的子数组个数。使用滑动窗口。

而这里需要计算的是子数组中不同元素的个数,

// todo

代码

性能

690.员工的重要性

目标

你有一个保存员工信息的数据结构,它包含了员工唯一的 id ,重要度和直系下属的 id 。

给定一个员工数组 employees,其中:

  • employees[i].id 是第 i 个员工的 ID。
  • employees[i].importance 是第 i 个员工的重要度。
  • employees[i].subordinates 是第 i 名员工的直接下属的 ID 列表。

给定一个整数 id 表示一个员工的 ID,返回这个员工和他所有下属的重要度的 总和。

示例 1:

输入:employees = [[1,5,[2,3]],[2,3,[]],[3,3,[]]], id = 1
输出:11
解释:员工 1 自身的重要度是 5 ,他有两个直系下属 2 和 3 ,而且 2 和 3 的重要度均为 3 。因此员工 1 的总重要度是 5 + 3 + 3 = 11 。

示例 2:

输入:employees = [[1,2,[5]],[5,-3,[]]], id = 5
输出:-3
解释:员工 5 的重要度为 -3 并且没有直接下属。因此,员工 5 的总重要度为 -3。

说明:

  • 1 <= employees.length <= 2000
  • 1 <= employees[i].id <= 2000
  • 所有的 employees[i].id 互不相同。
  • -100 <= employees[i].importance <= 100
  • 一名员工最多有一名直接领导,并可能有多名下属。
  • employees[i].subordinates 中的 ID 都有效。

思路

有一个数据结构 Employee,属性有 id、重要性、下属id集合。现在要查找给定id员工的重要性,即自身及下属的重要性之和。

直接使用map映射id与员工对象,使用dfs或者bfs搜索并累加重要性即可。

代码


/**
 * @date 2024-08-26 14:59
 */
public class GetImportance690 {
    class Employee {
        public int id;
        public int importance;
        public List<Integer> subordinates;
    }

    public int getImportance(List<Employee> employees, int id) {
        Employee root = null;
        Map<Integer, Employee> map = new HashMap<>();
        for (Employee employee : employees) {
            map.put(employee.id, employee);
        }
        int res = 0;
        Queue<Employee> q = new ArrayDeque<>(employees.size());
        q.offer(map.get(id));
        while (!q.isEmpty()) {
            int size = q.size();
            for (int i = 0; i < size; i++) {
                Employee node = q.poll();
                res += node.importance;
                for (Integer subordinate : node.subordinates) {
                    q.offer(map.get(subordinate));
                }
            }
        }
        return res;
    }
}

性能

698.划分为k个相等的子集

目标

给定一个整数数组 nums 和一个正整数 k,找出是否有可能把这个数组分成 k 个非空子集,其总和都相等。

示例 1:

输入: nums = [4, 3, 2, 3, 5, 2, 1], k = 4
输出: True
说明: 有可能将其分成 4 个子集(5),(1,4),(2,3),(2,3)等于总和。

示例 2:

输入: nums = [1,2,3,4], k = 3
输出: false

说明:

  • 1 <= k <= len(nums) <= 16
  • 0 < nums[i] < 10000
  • 每个元素的频率在 [1,4] 范围内

思路

给定一个数组 nums,判断能否将其划分为 k 个非空子集,要求每个子集的和都相等。

与数组中元素顺序无关可以排序。数组元素的和是可以枚举的,它一定大于等于数组中的最大元素。和确定了之后就可以从后向前判断能否组成这个和,如果不能则立即返回。

// todo

代码

性能

3146.两个字符串的排列差

目标

给你两个字符串 s 和 t,每个字符串中的字符都不重复,且 t 是 s 的一个排列。

排列差 定义为 s 和 t 中每个字符在两个字符串中位置的绝对差值之和。

返回 s 和 t 之间的 排列差 。

示例 1:

输入:s = "abc", t = "bac"
输出:2
解释:
对于 s = "abc" 和 t = "bac",排列差是:
"a" 在 s 中的位置与在 t 中的位置之差的绝对值。
"b" 在 s 中的位置与在 t 中的位置之差的绝对值。
"c" 在 s 中的位置与在 t 中的位置之差的绝对值。
即,s 和 t 的排列差等于 |0 - 1| + |2 - 2| + |1 - 0| = 2。

示例 2:

输入:s = "abcde", t = "edbac"
输出:12
解释: s 和 t 的排列差等于 |0 - 3| + |1 - 2| + |2 - 4| + |3 - 1| + |4 - 0| = 12。

说明:

  • 1 <= s.length <= 26
  • 每个字符在 s 中最多出现一次。
  • t 是 s 的一个排列。
  • s 仅由小写英文字母组成。

思路

有两个仅由小写英文字母组成的字符串,同一字符串中的字母各不相同,并且组成两个字符串所用的字母相同,只是排列可能不同。求这两个字符串中相同字母位置之差的绝对值之和。

使用长度为26的数组记录每个字母在字符串s中的位置,然后遍历字符串t,计算位置之差的绝对值并累加求和即可。

代码


/**
 * @date 2024-08-24 15:01
 */
public class FindPermutationDifference3146 {
    public int findPermutationDifference(String s, String t) {
        int res = 0;
        int[] pos = new int[26];
        int n = s.length();
        for (int i = 0; i < n; i++) {
            pos[s.charAt(i) - 'a'] = i;
        }
        for (int i = 0; i < n; i++) {
            res += Math.abs(pos[t.charAt(i) - 'a'] - i);
        }
        return res;
    }
}

性能

3145.大数组元素的乘积

目标

一个非负整数 x强数组 指的是满足元素为 2 的幂且元素总和为 x 的最短有序数组。下表说明了如何确定 强数组 的示例。可以证明,x 对应的强数组是独一无二的。

数字 二进制表示 强数组
1 00001 [1]
8 01000 [8]
10 01010 [2, 8]
13 01101 [1, 4, 8]
23 10111 [1, 2, 4, 16]

我们将每一个升序的正整数 i (即1,2,3等等)的 强数组 连接得到数组 big_numsbig_nums 开始部分为 [1, 2, 1, 2, 4, 1, 4, 2, 4, 1, 2, 4, 8, ...]

给你一个二维整数数组 queries ,其中 queries[i] = [fromi, toi, modi] ,你需要计算 (big_nums[fromi] * big_nums[fromi + 1] * ... * big_nums[toi]) % modi

请你返回一个整数数组 answer ,其中 answer[i] 是第 i 个查询的答案。

示例 1:

输入:queries = [[1,3,7]]
输出:[4]
解释:
只有一个查询。
big_nums[1..3] = [2,1,2] 。它们的乘积为 4。结果为 4 % 7 = 4。

示例 2:

输入:queries = [[2,5,3],[7,7,4]]
输出:[2,2]
解释:
有两个查询。
第一个查询:big_nums[2..5] = [1,2,4,1] 。它们的乘积为 8 。结果为 8 % 3 = 2。
第二个查询:big_nums[7] = 2 。结果为 2 % 4 = 2。

说明:

  • 1 <= queries.length <= 500
  • queries[i].length == 3
  • 0 <= queries[i][0] <= queries[i][1] <= 10^15
  • 1 <= queries[i][2] <= 10^5

思路

// todo

代码

性能

3133.数组最后一个元素的最小值

目标

给你两个整数 n 和 x 。你需要构造一个长度为 n 的 正整数 数组 nums ,对于所有 0 <= i < n - 1 ,满足 nums[i + 1] 大于 nums[i] ,并且数组 nums 中所有元素的按位 AND 运算结果为 x 。

返回 nums[n - 1] 可能的 最小 值。

示例 1

输入:n = 3, x = 4
输出:6
解释:
数组 nums 可以是 [4,5,6] ,最后一个元素为 6 。

示例 2

输入:n = 2, x = 7
输出:15
解释:
数组 nums 可以是 [7,15] ,最后一个元素为 15 。

说明

  • 1 <= n, x <= 10^8

思路

构造一个长度为 n 的单调递增数组 nums,要求数组所有元素按位与的结果为正整数 x,返回 nums[n - 1] 的最小值。

要使元素按位与的结果为 x,那么 x 中bit位为1的位置在元素中也必须为1。我们可以找到 x 中bit位为0的位置,从最低位开始置1,如果还不凑不够数组个数就将最高位置1,重新从最低位开始置1。

刚开始是按照上面的思路模拟着写的,非常容易出错。调试了半天发现,其实就是将 n - 1 的二进制表示填入 x 的二进制表示中的bit位为0的位置,如果不够就借用高位的0。之所以是 n -1 是因为 x 本身也算一个元素,0 ~ n - 1n 个元素。

官网题解也正是这个思路,不过实现简洁多了。主要是刚开始没有想清楚,其实根本不用讨论低位/高位,直接填就行了。

代码


/**
 * @date 2024-08-22 10:12
 */
public class MinEnd3133 {

    public long minEnd(int n, int x) {
        List<Integer> zeroIndex = new ArrayList<>(63);
        int i = 0;
        int tmp = x;
        // 记录0的位置
        while (tmp > 0) {
            if ((tmp & 1) == 0) {
                zeroIndex.add(i);
            }
            tmp >>= 1;
            i++;
        }
        long res = x;
        int zeroCnt = zeroIndex.size();
        long highBitsCnt;
        // 如果不含0,那么只能填高位
        if (zeroCnt == 0) {
            // 这个不应该全填1,应填n-1的二进制表示
            highBitsCnt = n - 1;
            while (highBitsCnt > 0) {
                if ((highBitsCnt & 1) == 1) {
                    res |= 1L << i;
                }
                i++;
                highBitsCnt >>= 1;
            }
            return res;
        }
        // 如果含0则计算其能表示的数字个数,下面是快速幂
        long oneLoopElementCnt = 1;
        tmp = zeroCnt;
        int base = 2;
        while (tmp > 0) {
            if ((tmp & 1) == 1) {
                oneLoopElementCnt = oneLoopElementCnt * base;
            }
            base *= base;
            tmp >>= 1;
        }
        // 低位需要填的个数
        long lowBitsCnt = n % oneLoopElementCnt - 1;
        if (lowBitsCnt == -1) {
            // 如果为-1,表示能够整除,将低位的0全置为1
            for (Integer index : zeroIndex) {
                res |= 1L << index;
            }
            // 如果低位能够填满数组则无需填高位
            if (n <= oneLoopElementCnt) {
                highBitsCnt = 0;
            } else {
                // 高位的计数需减1,是因为将低位全填1,这时已经完成了一个loop,例如n=4,x=2,oneLoopElementCnt 为2,n / oneLoopElementCnt = 2, (10 11) 110 111,我们只需要填充一个高位
                highBitsCnt = n / oneLoopElementCnt - 1;
            }
        } else {
            // 否则将需要填在低位的个数填入低位的0
            int j = 0;
            while (lowBitsCnt > 0) {
                if ((lowBitsCnt & 1) == 1) {
                    res |= 1L << zeroIndex.get(j);
                }
                j++;
                lowBitsCnt >>= 1;
            }
            // 如果低位能够填满数组则无需填高位
            if (n <= oneLoopElementCnt) {
                highBitsCnt = 0;
            } else {
                // 这里不需要减1,是因为n不能整除oneLoopElementCnt,多余位已经被舍去了,例如n=3,x=2,n / oneLoopElementCnt = 1
                highBitsCnt = n / oneLoopElementCnt;
            }
        }

        // 填充高位个数
        while (highBitsCnt > 0) {
            if ((highBitsCnt & 1) == 1) {
                // 出错点:如果写成1,会溢出,必须加上L
                res |= 1L << i;
            }
            i++;
            highBitsCnt >>= 1;
        }

        return res;
    }

}

性能