2.字符串
2.1两数相加
题目:
给定两个字符串形式的非负整数 num1
和num2
,计算它们的和并同样以字符串形式返回。
1 <= num1.length, num2.length <= 104
num1 和num2都只包含数字 0-9
num1 和num2都不包含任何前导零
输入:num1 = "11", num2 = "123"
输出:"134"
思路:
123
+999
-----
定义一个用来补位的变量 int add = 0;
定义一个StringBuffer类型用来存储结果 StringBuffer s = new StringBuffer();
第一次计算:
3+9+add = 12 add = 12/10 = 1 s.append(12%10)
第二次计算:
2+9+add = 11+1 = 12 add = 12/10 = 1 s.append(12%10)
第三次计算:
1+9+add = 10+1 = 11 add = 11/10 = 1 s.append(11%10)
由于add != 0 仍然要继续计算
0+0+add = 1 add = 1/10 = 0 s.append(1%10)
s为2211
s.reverse() 进行反转
s为1122
代码
class Solution {
public String addStrings(String num1, String num2) {
//定义x,y 均从字符串的末尾获取单个字符 进行+运算
int x = num1.length()-1;
int y = num2.length()-1;
//定义变量 用来存储进位的值
int add = 0;
//用来存储相加的结果
StringBuffer s = new StringBuffer();
//循环条件:num1.charAt(x)的下标>=0 或 num2.charAt(y) >= 0 或 仍有需要进位的值
while(x >= 0||y >=0||add != 0){
int m = x >= 0 ? num1.charAt(x)-'0':0;
int n = y >= 0 ? num2.charAt(y)-'0':0;
int result = m+n+add;
//result/10获取进位的值 例如 3/10 = 0 11/10 = 1
add = result/10;
//result%10 获取两个字符相加的和 例如 13%10 = 3 7%10 = 7
s.append(result%10);
//字符串num1,num2向前移动一位
x--;
y--;
}
//反转StringBuffer
s.reverse();
return s.toString();
}
}
2.2 两数相乘
题目:
给定两个以字符串形式表示的非负整数 num1
和 num2
,返回 num1
和 num2
的乘积,它们的乘积也表示为字符串形式。
输入: num1 = "2", num2 = "3"
输出: "6"
1 <= num1.length, num2.length <= 200
num1 和 num2 只能由数字组成。
num1 和 num2 都不包含任何前导零,除了数字0本身。
思路:
123
* 456
-------
456中的每一位与123进行相乘
定义一个变量carry用来保存进位的值
定义一个StringBuffer类型的变量temp 用来存储计算得到的值
定义一个String类型的变量res 存放每次乘法的结果 用来结果的相加和返回
int carry = 0;
StringBuffer temp = new StringBuffer();
以num2(456)的字符位数作为循环次数 从后往前循环
for(int i = num2.length()-1;i>=0;i--)
由乘法可知 num2的倒数第2个字符在进行乘法运算时,计算结果的最后一位需要补0 同理倒数第3个字符运算时,结果的最后两位需要补0
123
* 456
---------
738
6150
49200
写一个for循环,用来补0
循环:
当i = num2.length()-1时 j = 0 条件j<0 不用补0
当i = num2.length()-2时 j = 0 条件j<1 补1个0
当i = num2.length()-3时 j = 0 条件j<2 补2个0
for(int j = 0;j<num2.length()-1-i;j++){
temp.append("0");
}
拿到num2中各位的值
int n2 = num2.charAt(i)-'0';
循环:num2中某一位的值与num1中各位值相乘 每次的结果再相加
循环条件为 num1中字符的下标>=0 或 进位的值不为0
for(int j = num1.length()-1;j>=0||carry != 0;j--){
int n1 = j < 0 ? 0 : num1.charAt(j) - '0';
//(相乘后+进位值)%10 得到该位的值
int product = (n1 * n2 + carry) % 10;
temp.append(product);
//进位的值
carry = (n1 * n2 + carry) / 10;
}
// 将当前结果与新计算的结果求和作为新的结果
res = addStrings(res, temp.reverse().toString());
解法
/**
* 计算形式
* num1
* x num2
* ------
* result
*/
class Solution {
public String multiply(String num1, String num2) {
if(num1.equals("0") || num2.equals("0")){
return "0";
}
//存放返回的结果
String res = "0";
//以num2的字符数作为循环次数
for(int i = num2.length()-1;i>=0;i--){
//用来保存进位的值
int carry = 0;
StringBuffer temp = new StringBuffer();
//补0 例如123 * 23 23中的2和123相乘 结果中最后一位为0
//num2中 最后一位不需要补0 倒数第2位补1个0 倒数第3位补2个0 ...
for(int j = 0;j<num2.length()-1-i;j++){
temp.append("0");
}
//拿到num2中各位的值
int n2 = num2.charAt(i)-'0';
//num2中某一位的值与num1中各位值相乘
for(int j = num1.length()-1;j>=0||carry != 0;j--){
int n1 = j < 0 ? 0 : num1.charAt(j) - '0';
int product = (n1 * n2 + carry) % 10;
temp.append(product);
//进位的值
carry = (n1 * n2 + carry) / 10;
}
// 将当前结果与新计算的结果求和作为新的结果
res = addStrings(res, temp.reverse().toString());
}
return res;
}
public String addStrings(String num1, String num2) {
int x = num1.length()-1;
int y = num2.length()-1;
int add = 0;
StringBuffer s = new StringBuffer();
while(x >= 0||y >=0||add != 0){
int m = x >= 0 ? num1.charAt(x)-'0':0;
int n = y >= 0 ? num2.charAt(y)-'0':0;
int result = m+n+add;
add = result/10;
s.append(result%10);
x--;
y--;
}
s.reverse();
return s.toString();
}
}
2.3 去除重复字母
题目:
给你一个字符串 s
,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。
输入:s = "bcabc"
输出:"abc"
输入:s = "cbacdcbc"
输出:"acdb"
1 <= s.length <= 104
s
由小写英文字母组成
思路:
字典序即 abcdefg...字母的顺序
用栈来存放去重后的字符
定义一个boolean[] b 用来表示 某个字符是否在栈中
例如 b['a'] = true 表示该字符在栈中
特殊情况,栈顶字符的字典序大于 当前循环遍历的字符 但栈顶字符在整个字符串中只有一个 所以栈顶字符放在前边
解法
class Solution {
public String removeDuplicateLetters(String s) {
//用来标志stack中是否已有此字符
boolean[] b = new boolean[256];
//用来计算s中此字符出现的次数
int[] counts = new int[256];
Stack<Character> stack = new Stack<>();
char[] c = s.toCharArray();
//计算得到每个字符出现的次数
for(int i = 0;i<c.length;i++){
if(counts[c[i]] == 0){
counts[c[i]] = 1;
} else {
counts[c[i]] = counts[c[i]]+1;
}
}
for(int i = 0;i<c.length;i++){
char cc = c[i];
//每遍历一个字符 字符出现的次数-1
counts[cc]--;
//该字符已在栈中,下一轮循环
if(b[cc]){
continue;
} else {
//遍历栈,栈顶字符字典序 > 当前字符字典序 如果栈顶字符出现次数为0 结束循环 否则 栈顶出栈 标记出栈字符不在栈中
while(!stack.isEmpty() && stack.peek() > cc){
if(counts[stack.peek()] == 0){
break;
}
b[stack.pop()] = false;
}
}
stack.push(cc);
b[cc] = true;
}
StringBuffer str = new StringBuffer();
while(!stack.isEmpty()){
str.append(stack.peek());
stack.pop();
}
return str.reverse().toString();
}
}
2.4 使括号有效的最少添加(921)
思路:
判断参数 需要添加多少个(或),设字符串成为有效的括号
例如 参数()) 需要添加一个( 变成(()) 或 ()() 不关心添加到哪个位置 只需要返回添加符号的个数
将字符串转成char[] 遍历数组 定义n1,n2分别记录要添加的(和)的数量
发现一个'(' 则n2+1( 需要一个')' )
发现一个')' n2-1 当n2 = -1时 说明'(' 不够 需要添加一个'(' n1+1 添加完一个'(' 左右括号抵消 n2 = 0 最终返回结果为n1+n2
解法
class Solution {
public int minAddToMakeValid(String s) {
int n1 = 0;
int n2 = 0;
char[] c = s.toCharArray();
for(char cc: c){
//如果当前字符为( 则需要的)数量+1
if(cc == '('){
n2++;
}
//如果当前字符为) 则需要的)数量-1
if(cc == ')'){
n2--;
//如果当前)数量 = -1 说明需要(的数量+1 由于左右()抵消 所以)需要的数量置为0
if(n2 == -1){
n2 = 0;
n1++;
}
}
}
return n1+n2;
}
}
2.5 平衡括号字符串的最少插入次数(1541)
思路:
()) 一个'(' 对应两个')' 称为平衡字符串
给定一个字符串 返回添加多少个字符,使其成为平衡字符串
例如 )( 返回结果为4 -> ())())
将字符串 转成char[] 遍历数组 定义n1 n2记录要添加'(' 和 ‘)’的数量
当遇到'('时 n2+=2 此时需要检查n2的数量 由于'('对应两个‘)’ 所以‘)’的需求量为偶数 如果此时‘)’的需求量为奇数时 需要再插入一个‘)’
if(n2 % 2 == 1){
n1++;
n2--;
}
当遇到‘)’时 n2-- 当n2 = -1时 说明‘)’太多了 需要插入一个'(' 同时n2 = 1
if(c[i] == ')'){
n2--;
if(n2 == -1){
n1++;
n2 = 1;
}
}
解法
class Solution {
public int minInsertions(String s) {
int n1 = 0;
int n2 = 0;
char[] c = s.toCharArray();
for(int i = 0;i<c.length;i++){
if(c[i] == '('){
n2+=2;
//如果对于)的需求量为奇数 由于(对应两个) 所以)的需求量为偶数 当)需求量为奇数时,需要插入一个)
if(n2 %2 == 1){
n1++;
n2--;
}
}
if(c[i] == ')'){
n2--;
//说明)太多了
if(n2 == -1){
//插入一个(
n1++;
//由于( 对应两个) 原本)数量为-1 添加一个( 后 )数量修改为1
n2 = 1;
}
}
}
return n2+n1;
}
}