数据结构算法——字符串问题(以LeetCode字符串题为例)

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. 去除重复字母​​​​​​​

        力扣icon-default.png?t=M4ADhttps://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) 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值