问题描述:切分数组

给定一个整数数组 nums ,小李想将 nums 切割成若干个非空子数组,使得每个子数组最左边的数和最右边的数的最大公约数大于 1 。为了减少他的工作量,请求出最少可以切成多少个子数组。

示例 1:

输入:nums = [2,3,3,2,3,3]    输出:2

解释:最优切割为 [2,3,3,2] 和 [3,3] 。第一个子数组头尾数字的最大公约数为 2 ,第二个子数组头尾数字的最大公约数为 3 。

示例 2:

输入:nums = [2,3,5,7]    输出:4

解释:只有一种可行的切割:[2], [3], [5], [7]

限制:

1 <= nums.length <= 10^5

2 <= nums[i] <= 10^6


动态规划
假设 nums[0] - nums[i-1](最少可以切成多少个子数组)已知。会有2种情况

已知的组 + nums[i]这一组,就是 f[i] = f[i-1]+1
nums[i] 和 nums[0] - nums[i-1]间的某一个是的公约数大于1。
设 这某一个数的下标是j 所以 f[i] = f[j-1] + 1
预置一份 1 - 10^6内数字n的因子数组 minPrime[n]

对数组元素进行因子分解,并记录得到这个因子的位置(posMap)

设 n下标的元素是6 因子分解后记录的位置,posMap[2]=n, posMap[3]=n。
当其他元素分解出为2的因子时,获取$posMap[2]记录中的位置n。
所以此处可切,得到dp[n-1]时的结果 + 1。

例:
[10,6,3,5,7]
posMap - 记录得到这个因子的位置
dp     - 记录执行到位置的次数
  10
  posMap[2] = 0;
  posMap[5] = 0;
  dp[0] = 1;
  ------------------------------------------
  10, 6
  posMap[3] = 1;
  dp[1] = 1;     // 已出现相同质因数2位置在0,0-1可以分,零开始直接 dp[1] = 1
  ------------------------------------------
  10, 6, 3
  dp[2] = 2;     // 已出现相同质因数3位置在1。1-2可以分,取dp[1]记录值+1
  ------------------------------------------
  10, 6, 3, 5
  dp[3] = 1;     // 已出现相同质因数5位置在0。0-2可以分,零开始直接 dp[3] = 1
  ------------------------------------------
  10, 6, 3, 5, 7
  posMap[7] = 4;
  dp[4] = 2;     // posMap未出现过7,取上次dp记录值+1
补课:

因子:假如整数n除以m,结果是无余数的整数,那么m就是n的因子。一个整数n的因子数为包含它自身的所有因子个数。例,12的因子数为6(1,2,3,4,6,12)
质数:质数又被称为素数,是指一个大于1的自然数,除了1和它自身外,不能被其它自然数整除的数叫做质数,其个数是无穷的,具有许多独特的性质,现如今多被用于密码学上。
约数:又称因数。整数a除以整数b(b≠0) 商是整数而没有余数,a能被b整除 或 b能整除a。a称为b的倍数,b称为a的约数。
互质:互质是公约数只有1的两个整数,叫做互质整数。公约数只有1的两个自然数,叫做互质自然数,后者是前者的特殊情形。
公约数:亦称“公因数”。它是一个能被若干个整数同时均整除的整数。如果一个整数同时是几个整数的约数,称这个整数为它们的“公约数”;公约数中最大的称为最大公约数。1总是它们的公因数。
质因数:在数论里是指能整除给定正整数的 质数。根据算术基本定理,不考虑排列顺序的情况下,每个正整数都能够以唯一的方式表示成它的质因数的乘积。

例:
  99的因数  - 1,3,9,11,33,99
  99的质因数- 3,11
  33的因数  - 1,3,11,33
  33的质因数- 3,11
  99和33的公约数 - 3,11

作者:FlagMain


class Solution {

    public int splitArray(int[] nums) {
        // 预置 minPrime
        int[] minPrime = new int[1000000 + 1];
        for (int i = 2; i < minPrime.length; i++) {
            if (minPrime[i] < 2) {
                for (int j = i; j < minPrime.length; j += i) {
                    minPrime[j] = i;
                }
            }
        }

        // 记录执行到位置的次数
        int[] dp = new int[nums.length];
        // 记录因子位置
        Map<Integer,Integer> posMap=new HashMap<Integer,Integer>();

        for (int i = 0; i < nums.length; i++) {
            // 预设次数
            dp[i] = i > 0 ? dp[i - 1] + 1 : 1;

            // 分解 $nums[$i] 的因子;
            int n = nums[i];
            while (n > 1) {
                int factor = minPrime[n];
                int minIndex = i;
                if (posMap.containsKey(factor)) {
                    minIndex = posMap.get(factor);
                }else {
                     posMap.put(factor, minIndex);
                }

                if (minIndex == 0) {
                    dp[i] = 1;
                } else {
                    // 和 已记录处理的位置+1 对比去一个低的
                    if ( ( dp[minIndex - 1] + 1 ) < dp[i] ){
                        dp[i] = dp[minIndex - 1] + 1;
                    }
                }
                // 更新posMap
                if (dp[i] < dp[minIndex]) {
                    posMap.put(factor, i);
                }
                n = n / factor;
            }
        }
        return dp[nums.length - 1];
    }
}