字符串变换 482、6、68

482. 密钥格式化(简单)(20mins)

给定一个许可密钥字符串 s,仅由字母、数字字符和破折号组成。字符串由 n 个破折号分成 n + 1 组。你也会得到一个整数 k 。

我们想要重新格式化字符串 s,使每一组包含 k 个字符,除了第一组,它可以比 k 短,但仍然必须包含至少一个字符。此外,两组之间必须插入破折号,并且应该将所有小写字母转换为大写字母。

返回 重新格式化的许可密钥 。

示例 1:

输入:S = "5F3Z-2e-9-w", k = 4
输出:"5F3Z-2E9W"
解释:字符串 S 被分成了两个部分,每部分 4 个字符;
     注意,两个额外的破折号需要删掉。

示例 2:

输入:S = "2-5g-3-J", k = 2
输出:"2-5G-3J"
解释:字符串 S 被分成了 3 个部分,按照前面的规则描述,第一部分的字符可以少于给定的数量,其余部分皆为 2 个字符。

提示:

  • 1 <= s.length <= 105
  • s 只包含字母、数字和破折号 '-'.
  • 1 <= k <= 104

解法一、从后往前遍历分组

 意想不到的用时 why

class Solution {
    public static String licenseKeyFormatting(String s, int k) {
        int ptr = s.length()-1;
        StringBuffer sb = new StringBuffer();
        s = s.toUpperCase();
        while(ptr >=0){
            for(int i = 0;i < k;i++){
                if(ptr < 0) break;
                if(s.charAt(ptr)!= '-'){
                    sb.insert(0,s.charAt(ptr));
                }else{
                    i--;
                }
                ptr--;
            }
            sb.insert(0,'-');
        }
        while(sb.length() > 0 && sb.charAt(0) == '-')sb.delete(0,1);
        return sb.toString();
    }
}

解法二、解法一优化

这个不再是循环四次一添加’-‘,而是手动添加计时器,这样最后也最多多出来一个。学会了。。。

class Solution {
    public String licenseKeyFormatting(String s, int k) {
        StringBuilder ans = new StringBuilder();
        int cnt = 0;

        for (int i = s.length() - 1; i >= 0; i--) {
            if (s.charAt(i) != '-') {
                cnt++;
                ans.append(Character.toUpperCase(s.charAt(i)));
                if (cnt % k == 0) {
                    ans.append("-");
                }
            }
        }
        if (ans.length() > 0 && ans.charAt(ans.length() - 1) == '-') {
            ans.deleteCharAt(ans.length() - 1);
        }
        
        return ans.reverse().toString();
    }
}

作者:力扣官方题解
链接:https://leetcode.cn/problems/license-key-formatting/solutions/1029860/mi-yao-ge-shi-hua-by-leetcode-solution-xnae/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 
6. Z 字形变换(中等)(30mins)

将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。

比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:

P   A   H   N
A P L S I I G
Y   I   R

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"

请你实现这个将字符串进行指定行数变换的函数:

string convert(string s, int numRows);

示例 1:

输入:s = "PAYPALISHIRING", numRows = 3
输出:"PAHNAPLSIIGYIR"
示例 2:
输入:s = "PAYPALISHIRING", numRows = 4
输出:"PINALSIGYAHRPI"
解释:
P     I    N
A   L S  I G
Y A   H R
P     I

示例 3:

输入:s = "A", numRows = 1
输出:"A"

提示:

  • 1 <= s.length <= 1000
  • s 由英文字母(小写和大写)、',' 和 '.' 组成
  • 1 <= numRows <= 1000

解法一、下标操作

k是循环数,如上图中,PAYPAL是一组循环,ISHIRI是一组循环,NG是一组循环。对于非第一行也非最后一行,固定有两个数;对于第一行和最后一行,一个数即可。

下图来自官解评论区~

public static String convert(String s, int numRows) {
        if(numRows == 1) return s;
        int k = 2 * numRows - 2;
        int len = s.length();
        StringBuffer sb = new StringBuffer();
        for(int i = 0;i < numRows;i++){
            int ptr = i;
            if(i != 0 && i != numRows -1){
                while(ptr < len){
                    sb.append(s.charAt(ptr));
                    if(ptr + k - 2 *i < len)sb.append(s.charAt(ptr + k - 2 *i));
                    ptr += k;
                }
            }else{
                while(ptr < len){
                    sb.append(s.charAt(ptr));
                    ptr += k;
                }
            }
        }
        return sb.toString();
    }

 

解法二、也是下标操作

解法一是假定它已经填好,对于新字符串的每个字符,映射找到原来的下标。这个是按s的原顺序遍历,靠原有字符映射出新字符的下标,把每个字符发配到该有的地方jpg。flag控制方向,到了边缘则返回,最后拼接每一行的字符串。

比上个慢1s,应该是用了ArrayList的缘故

class Solution {
    public String convert(String s, int numRows) {
        if(numRows < 2) return s;
        List<StringBuilder> rows = new ArrayList<StringBuilder>();
        for(int i = 0; i < numRows; i++) rows.add(new StringBuilder());
        int i = 0, flag = -1;
        for(char c : s.toCharArray()) {
            rows.get(i).append(c);
            if(i == 0 || i == numRows -1) flag = - flag;
            i += flag;
        }
        StringBuilder res = new StringBuilder();
        for(StringBuilder row : rows) res.append(row);
        return res.toString();
    }
}

作者:Krahets
链接:https://leetcode.cn/problems/zigzag-conversion/solutions/21610/zzi-xing-bian-huan-by-jyd/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

68. 文本左右对齐(困难)(45mins)

class Solution {
    public static List<String> fullJustify(String[] words, int maxWidth) {
        int num = words.length;
        int p = 0,q = 0;
        List<String> res = new ArrayList<>();
        if(num == 1){//处理单个的情况
            while(maxWidth > words[0].length()){
                words[0]=words[0]+' ';
            }
            res.add(words[0]);
            return res;
        }
        while(q < num){
            int lenSum = words[p].length()+1;
            while(q+1 < num && lenSum + words[q+1].length() <= maxWidth){
                lenSum += words[q+1].length() + 1;
                q++;
            }
            int space=0,spaceAver=0,SpaceNum = 0;
            if(q != p){
                space = (maxWidth - (lenSum - (q + 1 - p)));
                spaceAver = space/(q - p);//每个空格位的基数
                SpaceNum = space - spaceAver * (q - p);//需要+1的数量
            }
            StringBuffer sb = new StringBuffer();
            for(int i = p;i <= q;i++){
                if(p==q || q == num -1){//最后一行
                    while(p!=q){
                        sb.append(words[p]);
                        sb.append(" ");
                        p++;
                    }
                    sb.append(words[q]);
                    while(sb.length() < maxWidth){
                        sb.append(" ");
                    }
                    break;
                }else {//其他情况
                    sb.append(words[i]);
                    if(i != q)for(int j = 0;j < spaceAver;j++)sb.append(" ");
                    if(SpaceNum > 0){
                        sb.append(" ");
                        SpaceNum--;
                    }
                }
            }
            res.add(sb.toString());
            p=q+1;
            q=p;
        }
        return res;
    }
}

解法一、贪心,模拟

借用了chat写一下注释~

初始化和简单情况处理

  1. 变量初始化:

    • num 存储单词的数量。

    • p 和 q 用于追踪当前行的开始和结束单词的索引。

    • res 用于存储结果的每一行。

  2. 单个单词处理:

    • 如果只有一个单词,直接将其后面填充空格至 maxWidth,然后添加到结果列表中。

主循环

  1. 计算行:

    • 使用两个指针 p 和 q 追踪当前正在处理的行的起始和结束单词。

    • lenSum 初始化为第一个单词的长度加上一个额外的空格(这个空格用于单词之间的分隔)。

    • 内部循环计算当前行可以容纳的单词。条件 lenSum + words[q+1].length() <= maxWidth 确保添加下一个单词加上空格不会超过最大宽度。

空格分配

  1. 空格计算:

    • space 计算整行剩余的空格数。

    • spaceAver 计算平均每对单词间应有的空格数。

    • SpaceNum 计算需要额外添加一个空格的空格位置数量(即不能均匀分配的那部分空格)。

构建行字符串

  1. 构建行:

    • 使用 StringBuffer 构建行。

    • 如果是最后一行或者只有一个单词,则左对齐,右边填充空格。

    • 否则,根据计算出的 spaceAver 和 SpaceNum 分配空格。

更新指针和添加行

  1. 更新指针和添加结果:

    • 每处理完一行,更新 p 和 q 指针到下一行的起始位置。

    • 将构建好的行添加到结果列表 res 中。

返回结果

  1. 返回最终结果:

    • 循环结束后,返回包含所有行的列表 res

class Solution {
    public static List<String> fullJustify(String[] words, int maxWidth) {
        int num = words.length;
        int p = 0,q = 0;
        List<String> res = new ArrayList<>();
        if(num == 1){//处理单个的情况
            while(maxWidth > words[0].length()){
                words[0]=words[0]+' ';
            }
            res.add(words[0]);
            return res;
        }
        while(q < num){//此时肯定已经不是只有一个了
            int lenSum = words[p].length()+1;
            while(q+1 < num && lenSum + words[q+1].length() <= maxWidth){
                lenSum += words[q+1].length() + 1;
                q++;
            }
            int space=0,spaceAver=0,SpaceNum = 0;
            if(q != p){
                space = (maxWidth - (lenSum - (q + 1 - p)));
                spaceAver = space/(q - p);//每个空格位的基数
                SpaceNum = space - spaceAver * (q - p);//需要+1的数量
            }
            StringBuffer sb = new StringBuffer();
            for(int i = p;i <= q;i++){
                if(p==q || q == num -1){//最后一行
                    while(p!=q){
                        sb.append(words[p]);
                        sb.append(" ");
                        p++;
                    }
                    sb.append(words[q]);
                    while(sb.length() < maxWidth){
                        sb.append(" ");
                    }
                    break;
                }else {//其他情况
                    sb.append(words[i]);
                    if(i != q)for(int j = 0;j < spaceAver;j++)sb.append(" ");
                    if(SpaceNum > 0){
                        sb.append(" ");
                        SpaceNum--;
                    }
                }
            }
            res.add(sb.toString());
            p=q+1;
            q=p;
        }
        return res;
    }
}

 

 


碎碎念

  • 今天好标准的简中难(喂。
  • 基本考察代码的结构清晰性 有一些小的部分需要考虑,会影响到整体用时
  • 边界考虑一般就指针是否越数组界,字符串是不是空,有时候只有一长度的时候需要特殊考虑,然后有时候是『i = 0』 i+1往后看还是「i = 1」 i-1往前看也需要考虑
  • 这仨题其实还挺好玩的
  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值