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写一下注释~
初始化和简单情况处理
变量初始化:
num
存储单词的数量。
p
和q
用于追踪当前行的开始和结束单词的索引。
res
用于存储结果的每一行。单个单词处理:
如果只有一个单词,直接将其后面填充空格至
maxWidth
,然后添加到结果列表中。主循环
计算行:
使用两个指针
p
和q
追踪当前正在处理的行的起始和结束单词。
lenSum
初始化为第一个单词的长度加上一个额外的空格(这个空格用于单词之间的分隔)。内部循环计算当前行可以容纳的单词。条件
lenSum + words[q+1].length() <= maxWidth
确保添加下一个单词加上空格不会超过最大宽度。空格分配
空格计算:
space
计算整行剩余的空格数。
spaceAver
计算平均每对单词间应有的空格数。
SpaceNum
计算需要额外添加一个空格的空格位置数量(即不能均匀分配的那部分空格)。构建行字符串
构建行:
使用
StringBuffer
构建行。如果是最后一行或者只有一个单词,则左对齐,右边填充空格。
否则,根据计算出的
spaceAver
和SpaceNum
分配空格。更新指针和添加行
更新指针和添加结果:
每处理完一行,更新
p
和q
指针到下一行的起始位置。将构建好的行添加到结果列表
res
中。返回结果
返回最终结果:
循环结束后,返回包含所有行的列表
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往前看也需要考虑
- 这仨题其实还挺好玩的