Leetcode—— 乘积最大子数组 / 乘积小于K的子数组 / 除自身以外数组的乘积 / 字符串相乘

这篇博客介绍了几种数组和字符串的操作算法。包括求乘积最大子数组的遍历方法,乘积小于K的子数组的滑动窗口算法,以及数组中除自身以外的元素乘积的计算。此外,还详细解析了字符串相乘的两种模拟计算方法,分别是逐位相乘和创建临时数组存储结果。这些算法展示了在处理数组和字符串时的有效计算策略。
摘要由CSDN通过智能技术生成

1. 乘积最大子数组

在这里插入图片描述

(1)遍历

  • 遍历数组时计算当前最大值,不断更新
  • 令imax为当前最大值,则当前最大值为 imax = max(imax * nums[i], nums[i])
  • 由于存在负数,那么会导致最大的变最小的,最小的变最大的。
  • 因此还需要维护当前最小值imin,imin = min(imin * nums[i], nums[i])
  • 当负数出现时则imax与imin进行交换再进行下一步计算
  • 时间复杂度:O(n)
class Solution {
    public int maxProduct(int[] nums) {
        int max = Integer.MIN_VALUE, imax = 1, imin = 1;
        for(int i=0; i<nums.length; i++){
            if(nums[i] < 0){ 
              int tmp = imax;
              imax = imin;
              imin = tmp;
            }
            imax = Math.max(imax*nums[i], nums[i]);
            imin = Math.min(imin*nums[i], nums[i]);
            
            max = Math.max(max, imax);
        }
        return max;
    }
}

2. 乘积小于K的子数组

在这里插入图片描述

(1)滑动窗口

有这样一个规律:窗口内每加入一个数,如果乘积仍然小于k,则 满足条件的子数组数量 += 窗口内元素数量

例如 [1.2.3.4.5] , k = 7, 符合要求子数组数量为res=0;
[1] res += 子数组.length; res=1;
[1,2] res =res+ 2 = 3
[1,2,3] res = res+3 =6

当遇到比k还大的元素时,直接重新在这个元素的右边继续开始计算
当窗口内元素乘积大于等于k时,在左边踢出元素,收缩窗口,直到乘积不再大于等于k

class Solution {
    public int numSubarrayProductLessThanK(int[] nums, int k) {
        if(k <= 1)
            return 0;
        int ans = 0, left = 0, right = 0;
        int mul = 1;

        while(right < nums.length) {
            //当left = right时就为1了,是小于k的不会越界
            //此时保证了mul是小于k的,相当于求右边界左边界为l,右边界为r的连续子数组的个数,
            //右边界是不断递增的,又因为是连续,所以满足条件的每种长度的子数组只会有一个
            //且右边界一直在变也就是不会重复
            //窗口内每加入一个数,如果乘积仍然小于k,则 满足条件的子数组数量 += 窗口内元素数量
            mul *= nums[right];
            while(mul >= k) {           //当遇到比k还大的元素时,直接重新在这个元素的右边继续开始计算
                mul /= nums[left];      //当窗口内元素乘积mul大于等于k时,在左边踢出元素,收缩窗口,直到乘积不再大于等于k
                left++;
            }
            ans += (right - left + 1);      
            right++;
        }
        return ans;
    }
}

3. 除自身以外数组的乘积

在这里插入图片描述

(1)左边乘积 * 右边乘积(有点前缀和的思想)

原数组:       [1       2       3       4]
左部分的乘积:   1       1      1*2    1*2*3
右部分的乘积: 2*3*4    3*4      4      1
结果:        1*2*3*4  1*3*4   1*2*4  1*2*3*1
class Solution {
    public int[] productExceptSelf(int[] nums) {
        // 结果存储
        int[] res = new int[nums.length];
        int k = 1;
        
        // 首先存储每个数左边数的乘积
        for(int i = 0; i < res.length; i++){
            res[i] = k;		
            k = k * nums[i];		//K有点像前缀和的思想,存的是i之前的数的乘积
        }
        k = 1;
        // 然后在原有的基础上乘上每个数右边数的乘积
        for(int i = res.length - 1; i >= 0; i--){
            res[i] *= k;			//K有点像前缀和的思想,存的是i之前的数的乘积
            k *= nums[i];
        }
        return res;
    }
}

4. 字符串相乘

在这里插入图片描述

(1)模拟手算

在这里插入图片描述

class Solution {
    /**
    * 计算形式
    *    num1
    *  x num2
    *  ------
    *  result
    */
    public String multiply(String num1, String num2) {
        if (num1.equals("0") || num2.equals("0")) {
            return "0";
        }
        // 保存计算结果
        String res = "0";
        
        // num2 逐位与 num1 相乘
        for (int i = num2.length() - 1; i >= 0; i--) {
            int carry = 0;
            // 保存 num2 第i位数字与 num1 相乘的结果
            StringBuilder temp = new StringBuilder();
            // 补 0 
            for (int j = 0; j < num2.length() - 1 - i; j++) {
                temp.append(0);
            }
            int n2 = num2.charAt(i) - '0';
            
            // num2 的第 i 位数字 n2 与 num1 相乘
            for (int j = num1.length() - 1; j >= 0 || carry != 0; j--) {
                int n1 = j < 0 ? 0 : num1.charAt(j) - '0';
                int product = (n1 * n2 + carry) % 10;
                temp.append(product);
                carry = (n1 * n2 + carry) / 10;
            }
            // 将当前结果与新计算的结果求和作为新的结果
            res = addStrings(res, temp.reverse().toString());
        }
        return res;
    }

    /**
     * 对两个字符串数字进行相加,返回字符串形式的和
     */
    public String addStrings(String num1, String num2) {
        StringBuilder builder = new StringBuilder();
        int carry = 0;
        for (int i = num1.length() - 1, j = num2.length() - 1;
             i >= 0 || j >= 0 || carry != 0;
             i--, j--) {
            int x = i < 0 ? 0 : num1.charAt(i) - '0';
            int y = j < 0 ? 0 : num2.charAt(j) - '0';
            int sum = (x + y + carry) % 10;
            builder.append(sum);
            carry = (x + y + carry) / 10;
        }
        return builder.reverse().toString();
    }
}

(2)模拟

需要知道一个数学定理:

  • 两个长度分别为 n 和 m 的数相乘,长度不会超过 n + m。
    因此我们可以创建一个长度为 n + m 的数组 res 存储结果。
  • 另外,最后拼接结果时需要注意忽略前导零。
class Solution {
    public String multiply(String n1, String n2) {
        int n = n1.length(), m = n2.length();
        int[] res = new int[n + m];
        for (int i = n - 1; i >= 0; i--) {
            for (int j = m - 1; j >= 0; j--) {
                int a = n1.charAt(i) - '0';
                int b = n2.charAt(j) - '0';
                int r = a * b;
                r += res[i + j + 1];
                res[i + j + 1] = r % 10;
                res[i + j] += r / 10;
            }
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < n + m; i++) {
            if (sb.length() == 0 && res[i] == 0) continue;
            sb.append(res[i]);
        }
        return sb.length() == 0 ? "0" : sb.toString();
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yawn__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值