1. 字符串
字符串(String)是由零个或多个字符组成的有限序列,它是编程语言中表示文本的数据类型。
字符串与数组有很多相似之处,比如可以使用索引(下标)来得到一个字符。字符串,一般可以认为就是一个字符数组(char array)。不过字符串有其鲜明的特点,它的结构相对简单,但规模可能是非常庞大的。
在编程语言中,字符串往往由特定字符集内有限的字符组合而成。在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。
2. 字符串相加
力扣https://leetcode.cn/problems/add-strings/
2.1 题目描述
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。
你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。
示例:
输入:num1 = "456", num2 = "77"
输出: "533"
2.2 解决方法
把字符串按每个字符char一一拆开,相当于遍历整数上的每一个数位,然后通过“乘10叠加”的方式,就可以整合起来了,这相当于算术中的“竖式加法”。用carry表示进位,实现手动进位。
public class AddStrings {
public String addStrings(String num1, String num2) {
StringBuffer sb = new StringBuffer();
int i = num1.length() - 1;
int j = num2.length() - 1;
int carry = 0;
while (i >= 0 || j >= 0 || carry != 0) {
int n = i >= 0 ? num1.charAt(i) - '0' : 0;
int m = j >= 0 ? num2.charAt(j) - '0' : 0;
int sum = n + m + carry;
sb.append(sum % 10);
carry = sum / 10;
i--;
j--;
}
return sb.reverse().toString();
}
public static void main(String[] args) {
String num1 = "456";
String num2 = "77";
AddStrings addStrings = new AddStrings();
System.out.println(addStrings.addStrings(num1, num2));
}
}
复杂度分析
时间复杂度:O(max(len1,len2)),其中len1 =num1.length,len2 =num2.length。竖式加法的次数取决于较大数的位数。
空间复杂度:O(n)。解法中使用到了 StringBuffer,所以空间复杂度为 O(n)。
3. 字符串相乘
力扣https://leetcode.cn/problems/multiply-strings/
3.1 题目描述
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
示例:
输入:num1 = "123", num2 = "456"
输出: "56088"
3.2 解决方法
m位数乘以n位数,结果最多就是m+n位;所以可以用一个m+n长度的数组来保存计算结果。而且,某两个数位相乘,num1[i] x num2[j] 的结果(定义为两位数,一位数的话前面补0),其第一位位于 result[i+j],第二位位于 result[i+j+1]。
综上,遍历num1和num2中的每一位数,相乘后叠加到result的对应位上就可以了。
public class MultiplyStrings {
public String multiply(String num1, String num2){
if(num1.equals("0")||num2.equals("0")){
return "0";
}
int[] res = new int[num1.length()+num2.length()];
for(int i = num1.length()-1;i>=0;i--){
int x = num1.charAt(i)-'0';
for(int j= num2.length()-1;j>=0;j--){
int y = num2.charAt(j)-'0';
int product = x*y;
int temp = res[i+j+1] + product;
res[i+j+1] = temp%10;
res[i+j] += temp/10;
}
}
StringBuffer sb = new StringBuffer();
int start = res[0]==0 ? 1 : 0;
for(int i = start; i< res.length;i++){
sb.append(res[i]);
}
return sb.toString();
}
public static void main(String[] args) {
String num1 = "123", num2 = "456";
MultiplyStrings multiplyStrings = new MultiplyStrings();
System.out.println(multiplyStrings.multiply(num1, num2));
}
}
复杂度分析
时间复杂度:O(mn),其中 m 和 n 分别是 num1 和 num2的长度。需要计算num1 的每一位和 num2的每一位的乘积。
空间复杂度:O(m+n),需要创建一个长度为 m+n 的数组存储乘积。
4. 去除重复字母
力扣https://leetcode.cn/problems/remove-duplicate-letters/comments/
4.1 题目描述
给你一个字符串 s
,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。
示例:
输入:s = "cbacdcbc"
输出: "acdb"
4.2 解决方法
字符串之间比较跟数字之间比较是不太一样的:字符串比较,是从头往后一个字符一个字符比较的,哪个字符串大取决于两个字符串中第一个对应不相等的字符。所以,任意一个以 a 开头的字符串都大于任意一个以 b 开头的字符串。
为了得到最小字典序的结果,解题过程中,我们可以将最小的字符尽可能的放在前面,把前面出现的重复字母全部删除。这其实就是一个贪心策略。
综上:遇到一个新字符,如果比栈顶小,并且在新字符后面还有和栈顶一样的,就把栈顶的字符抛弃了
public class RemoveDuplicateLetters {
public String removeDuplicateLetters(String s){
Stack<Character> stack = new Stack<>();
HashMap<Character,Integer> map = new HashMap<>();
Set<Character> set = new HashSet<>();
for (int i = 0; i < s.length(); i++) {
map.put(s.charAt(i),i);
}
for(int i=0;i<s.length();i++){
char ch = s.charAt(i);
if(!set.contains(ch)){
while (!stack.isEmpty() && ch < stack.peek() && map.get(stack.peek())>i){
set.remove(stack.pop());
}
set.add(ch);
stack.push(ch);
}
}
StringBuffer sb = new StringBuffer();
for (Character character : stack) {
sb.append(character);
}
return sb.toString();
}
public static void main(String[] args) {
String s = "cbacdcbc";
RemoveDuplicateLetters removeDuplicateLetters = new RemoveDuplicateLetters();
System.out.println(removeDuplicateLetters.removeDuplicateLetters(s));
}
}
复杂度分析
时间复杂度:O(N)。虽然看起来是双重循环,但内循环的次数受栈中剩余字符总数的限制,因为栈中的元素不重复,不会超出字母表大小,因此最终复杂度仍为 O(N)。
空间复杂度:O(1)。看上去空间复杂度像是 O(N),但实际上并不是。首先,set中字符不重复,其大小会受字母表大小的限制,所以是O(1)。其次,只有 stack 中不存在的元素才会被压入,因此 stack 中的元素也唯一。所以最终空间复杂度为 O(1)