贪心算法之763划分字⺟母区间

75 阅读3分钟

什么是贪心算法

贪心算法是一种常用的算法思想,它是一种在每一步选择中都采取当前状态下最优的选择,从而希望导致全局最优解的算法。

贪心算法通常分为两种类型:

  1. 基于贪心策略的优化问题:该类问题中,需要对某个目标函数进行最大化或最小化的优化,而目标函数通常可以分解为若干个子问题,且每个子问题的最优解可以直接得到,因此我们可以采取贪心策略,即在每个子问题中都选择当前最优解,最终得到全局最优解。
  2. 基于贪心思想的组合优化问题:该类问题中,需要从一个集合中选择一部分元素,使得这些元素满足某些条件,并且达到某个优化目标。在这种问题中,我们也可以采取贪心思想,即在每一步中都选择当前最优的元素,最终得到一个可行解或最优解。

贪心算法的优点是简单、高效,容易实现和调试。但是,贪心算法不能保证得到全局最优解,因此在某些情况下可能会得到次优解或不可行解。因此,在使用贪心算法时需要注意问题的特殊性和局限性,确保问题可以适用贪心算法。

  1. 题目描述

字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的⽚段,同⼀字母最多出现在一个片段中。返回一个表示每个字符串片段的⻓度的列表。

例子

输⼊:S = "ababcbacadefegdehijhklij" 输出:[9,7,8]
解释:
划分结果为 "ababcbaca", "defegde", "hijhklij"。 每个字⺟最多出现在⼀一个⽚片段中。 像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的⽚段数较少。

提示

S的⻓度在[1, 500]之间。 S只包含小写字母'a' 到 'z' 。

  1. 思考

1 思路很简单,利用递归,不间断的去查找 2 利用贪⼼算法和双指针,⾸先使⽤一个数组存储所有字符出现的最后位置,然后利用双指针,一个指向子串开始的位置,⼀个指向子串结束的位置,然后不断查找,当发现一个字⺟已经到达了它在字符串串中的最后位置的时候,就是相当于发现了一个符合条件的⼦串。

代码很简单,稍微看下,就明白了

这⾥可以得到⼀个小提示,⼀旦涉及到字符串的时候,很⾃然的就要想到使⽤一个⻓度为26的数组来存储。

  1. 实现
/**
 * @param {string} S * @return {number[]} */
const getBigStr = (S, begin, res) => {
  let max = begin;
  const len = S.length;
  if (begin >= S.length) {
    return;
  }
  const lastIndex = S.lastIndexOf(S[begin]);
  if (lastIndex !== -1) {
    max = Math.max(max, lastIndex);
    let s1 = S.substring(begin, lastIndex + 1);
    for (let i = 1; i < s1.length; i++) {
      const newLastIndex = S.lastIndexOf(s1[i]);
      if (newLastIndex > max) {
        max = newLastIndex;
        s1 = S.substring(begin, max + 1);
      }
    }
    res.push(S.substring(begin, max + 1));
  } else {
    res.push(S[begin]);
    max = begin++;
  }
  return max;
};
export default (S) => {
  let res = [];
  let max = -1;
  while (max < S.length) {
    max = getBigStr(S, max + 1, res);
  }
  return res.map((item) => item.length);
};


export default (S) => {
  if (S == null || S.length === 0) {
    return null;
  }
  const list = [];
  // 记录每个字符出现在字符串串中的最后的位置 const map = new Array(26).fill(0);
  for (leti = 0; i < S.length; i++) {
    map[S.charCodeAt(i) - 97] = i;
  }
  // 记录每个⼦子串串出现的开始和结束 let last = 0;
  let start = 0;
  for (leti = 0; i < S.length; i++) {
    last = Math.max(last, map[S.charCodeAt(i) - 97]);
    if (last === i) {
      list.push(last - start + 1);
      start = last + 1;
    }
  }
  return list;
};

时间复杂度O(n), 空间复杂度O(1)