【C/C++】806. 写字符串需要的行数

193 阅读5分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第12天,点击查看活动详情


题目链接:806. 写字符串需要的行数

题目描述

我们要把给定的字符串 S 从左到右写到每一行上,每一行的最大宽度为 100 个单位,如果我们在写某个字母的时候会使这行超过了 100 个单位,那么我们应该把这个字母写到下一行。我们给定了一个数组 widths ,这个数组 widths[0] 代表 'a' 需要的单位, widths[1] 代表 'b' 需要的单位,..., widths[25] 代表 'z' 需要的单位。

现在回答两个问题:至少多少行能放下 S ,以及最后一行使用的宽度是多少个单位?将你的答案作为长度为 2 的整数列表返回。

注:

  • 字符串 S 的长度在 [1, 1000] 的范围。
  • S 只包含小写字母。
  • widths 是长度为 26 的数组。
  • widths[i] 值的范围在 [2, 10]

示例 1:

输入: 
widths = [10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10]
S = "abcdefghijklmnopqrstuvwxyz"
输出: [3, 60]
解释: 
所有的字符拥有相同的占用单位10。所以书写所有的26个字母,
我们需要2个整行和占用60个单位的一行。

示例 2:

输入: 
widths = [4,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10]
S = "bbbcccdddaaa"
输出: [2, 4]
解释: 
除去字母'a'所有的字符都是相同的单位10,并且字符串 "bbbcccdddaa" 将会覆盖 9 * 10 + 2 * 4 = 98 个单位.
最后一个字母 'a' 将会被写到第二行,因为第一行只剩下2个单位了。
所以,这个答案是2行,第二行有4个单位宽度。

题意整理

每个样例会给 26 个小写字母得宽度单位,然后给一个字符串,问这个字符串占多少行,最后一行的宽度单位是多少。

解题思路分析

题目描述很长,但是题意核心就是 模拟

  1. 模拟题我们需要多注意题目给的数据范围,保证所有情况都要考虑到。
  2. 虽然题目描述很长但也要耐心读完,保证对题目理解完整,不能为了省时间读一半题目,猜题目意思,而忽略了题目一些细节处理。
  3. 这里需要注意题目要求的 “每一行的最大宽度为 100 个单位”“如果我们在写某个字母的时候会使这行超过了 100 个单位,那么我们应该把这个字母写到下一行。” ,这里就限定了我们不能求出字符串总的宽度单位然后进行除法和模运算,只能按照题目要求一步一步模拟。
  4. 给定的字母宽度单位数组是按照顺序给定的,并且从下标 0 开始,并且题目给定的字符串只包含小写字母,那这里可以直接用字符串中的字符减去 'a' 得到该字符在数组中的下标,从而得到字符的宽度单位。widths[s[i] - 'a']就是 s[i] 的宽度单位了。

这里需要注意的是,虽然也可以用 map 记录每个字母的宽度单位,然后直接 map['字母'] 得到字母的宽度单位,但是map的时间复杂度为 O(log2n)O(log_2n) ,就算使用 unordered_map 时间复杂度为常数级别 O(1)O(1) ,而数组查询的时间复杂度为不带常数的 O(1)O(1) ,在效率上会快很多。

mapunordered_map 区别

std::map 对应的数据结构是红黑树。红黑树是一种近似于平衡的二叉查找树,里面的数据是有序的。在红黑树上做查找、插入、删除操作的时间复杂度为O(log2N)O(log_2N)

std::unordered_map 对应哈希表,哈希表的特点就是查找效率高,时间复杂度为常数级别 O(1)O(1) ,而额外空间复杂度则要高出许多。

所以对于需要高效率查询的情况,使用 std::unordered_map 容器,但是 std::unordered_map 对于迭代器遍历效率并不高。而如果对内存大小比较敏感或者数据存储要求有序的话,则可以用 std::map 容器。 复杂度分析

  • 时间复杂度:O(n)O(n),其中 nn 为字符串 ss 的长度。需要遍历一遍字符串 ss,求出行数。
  • 空间复杂度:O(1)O(1)

具体实现

从左到右遍历字符串 s 中的每个字母,row 表示书写所需的总行数,temp 表示当前行已经使用的宽度。当遍历到一个字母 c 时:

  • 如果 temp+widths[c]100temp + widths[c] \le 100 ,此时那么更新 temp = temp + widths[c] 并保持 row 不变;
  • 如果 temp+widths[c]>100temp + widths[c] > 100 ,此时需要另起新的一行,那么此时 row 的值加 1,并将 temp 置为 widths[c]

代码实现

class Solution {
public:
    vector<int> numberOfLines(vector<int>& widths, string s) {
        //设row为总行数,temp为当前宽度
        int row, temp;
        row = temp = 0;
        int n = s.length();
        for(int i = 0; i < n; i++){
            //如果当前宽度加上字符宽度没有超过100
            if(temp + widths[(s[i] - 'a')] <= 100){
                temp += widths[(s[i] - 'a')];
            }
            //宽度超过了100
            else{
                //另起一行
                temp = widths[(s[i] - 'a')];
                //总行数+1
                row++;
            }
        }
        //如果temp > 0表示虽然没满一行,但是也占了一行
        if(temp > 0) row++;
        return {row, temp};
    }
};

总结

该题为简单模拟题,需要注意 widths[s[i] - 'a'] 手法,灵活使用字符的 ASCII 码,在不同场合灵活使用 mapunordered_map ,另外就是注意题目细节,避免读错题目,读假题现象。

结束语

每个人的成长,都需要时间沉淀。很多人不愿意努力,觉得短时间看不到回报,就不再付出。可你要知道,你的每一次成长,都可能在未来某个节点,让你成为更耀眼夺目的自己。