算法篇03、字符串相关算法--栈、滑动窗口、哈希表等

212 阅读2分钟

本篇主要是字符串相关的算法题,涉及的解决方案有栈、滑动窗口、哈希表等

1、leetcode20--有效的括号

给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。

题解如下所示,本题可以使用栈这种数据结构解决,栈是一种后进先出的数据结构;

因此我们循环遍历字符串,如果是左半部分的括号,就把对应右半部分的括号放入栈,这样当遍历到右半部分的括号时,如果这个字符串代表合法的字符串,那么栈顶的元素就应该跟刚刚遍历到的这个右半部分的括号相等,如果不等或者栈为空,说明不合法;当遍历完字符串后,如果是合法的字符串那么栈中应该没有字符了,因为都应该匹配完了,如果还有说明不合法;

//leetcode 20 有效的括号
public boolean isValid(String s) {
    Stack<Character> stack = new Stack<>();
    for (int i = 0; i < s.length(); i++) {
        if (s.charAt(i) == '{') {
            stack.push('}');
        } else if (s.charAt(i) == '[') {
            stack.push(']');
        } else if (s.charAt(i) == '(') {
            stack.push(')');
        } else {
            if (stack.isEmpty() || stack.pop() != s.charAt(i)) {
                return false;
            }
        }
    }
    return stack.isEmpty();
}

2、leetcode3--无重复字符的最长字串

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

题解如下所示,本题也是使用滑动窗口的典型题目;设置一个[l...r]的滑动窗口,维护窗口中的元素不发生重复;我们新建一个HashMap,key为字符,value为字符出现的次数,我们让滑动窗口的右侧右移依次向右遍历字符串,把字符和字符的次数保存在HashMap中,如果遍历到的字符在HashMap中的value大于1说明发生了重复,我们就让滑动窗口的左侧右移直到没有重复字符为止;此时如果滑动窗口的大小比当前保存的结果还大,就更新为滑动窗口的大小;

//leetcode 3 无重复字符的最长字串
public int lengthOfLongestSubstring(String s) {
    HashMap<Character, Integer> windows = new HashMap<>();
    char[] chars = s.toCharArray();
    int l = 0;
    int r = 0;
    int res = 0;
    while (r < chars.length) {
        char c = chars[r];
        r++;
        windows.put(c, windows.getOrDefault(c, 0) + 1);
        while (windows.get(c) > 1) {
            char removeChar = chars[l];
            l++;
            windows.put(removeChar, windows.get(removeChar) - 1);
        }
//            if (r - l > res) {
//                res = r - l;
//            }
        res = Math.max(res, r - l);
    }
    return res;
}

3、leetcode387--字符串中的第一个唯一字符

给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。 你可以假定该字符串只包含小写字母。

题解如下所示,这题求次数的题,第一反应就是哈希表,此题哈希表当然能解决,不过题目中说只包含小写字母,因此我们可以使用一个int型的数组,数组大小为26,用来记录每个小写字母出现的次数;先遍历一遍字符串,记录好次数;然后再遍历一遍字符串,出现此处等于1的第一个字符就是我们求的字符,直接返回索引即可;

//leetcode 387 字符串中的第一个唯一字符
public int firstUniqChar(String s) {
    int[] arr = new int[26];
    for (int i = 0; i < s.length(); i++) {
        arr[s.charAt(i) - 'a']++;
    }
    for (int i = 0; i < s.length(); i++) {
        if (arr[s.charAt(i) - 'a'] == 1) {
            return i;
        }
    }
    return -1;
}

4、leetcode76--最小覆盖子串

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。

注意:如果 s 中存在这样的子串,我们保证它是唯一的答案。

题解如下所示,此题也是一个典型的使用滑动窗口解决的问题,不过比一般的滑动窗口稍微复杂一点;本题的思想就是维护一个[left...right]的滑动窗口,使该窗口内的子串覆盖字符串t的所有字符;

首先新建一个need的HashMap,将被覆盖的字符串t的所有字符以及字符出现的次数存入此HashMap;然后右指针依次向右遍历源字符串s,如果字符被完全覆盖了,将结果修改为滑动窗口的大小和当前保存的结果中的较小值,然后移动左指针往右,看是否还满足完全覆盖字符,如果满足继续比较滑动窗口和当前结果;不满足跳出循环进行下一次比较;

//leetcode 76 最小覆盖子串
//通过左右指针构造一个滑动窗口的典型应用
public String minWindow(String s, String t) {
    HashMap<Character, Integer> need = new HashMap<>();
    for (int i = 0; i < t.length(); i++) {
        char c = t.charAt(i);
        need.put(c, need.getOrDefault(c, 0) + 1);
    }
    HashMap<Character, Integer> window = new HashMap<>();
    int left = 0, right = 0;
    int valid = 0;
    int start = 0;
    int len = Integer.MAX_VALUE;

    while (right < s.length()) {
        char c = s.charAt(right);
        right++;
        if (need.containsKey(c)) {
            window.put(c, window.getOrDefault(c, 0) + 1);
            if (window.get(c).equals(need.get(c))) {
                valid++;
            }
        }

        while (valid == need.size()) {
            if (right - left < len) {
                start = left;
                len = right - left;
            }
            char d = s.charAt(left);
            left++;
            if (need.containsKey(d)) {
                if (window.get(d).equals(need.get(d))) {
                    valid--;
                }
                window.put(d, window.get(d) - 1);
            }
        }
    }

    return len == Integer.MAX_VALUE ? "" : s.substring(start, start + len);
}

5、leetcode5--最长回文子串

给你一个字符串 s,找到 s 中最长的回文子串。

题解如下所示,palindrome子函数是求源字符串的回文子字符串,因为字符串长度有偶数和基数两种情况,所以我们分了两种情况来求s1表示长度为奇数的情况,s2表示字符串长度为偶数的情况,最后取s1和s2的最大值即可;

//leetcode 5 最长回文子串
public String longestPalindrome(String s) {
    String res = "";
    for (int i = 0; i < s.length(); i++) {
        String s1 = palindrome(s,i,i);
        String s2 = palindrome(s,i,i+1);
        res = res.length() > s1.length() ? res : s1;
        res = res.length() > s2.length() ? res : s2;
    }
    return res;
}

public String palindrome(String s, int l, int r) {
    while (l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)) {
        l--;
        r++;
    }
    return s.substring(l+1,r);
}

题目来源出处

来源:力扣(LeetCode) 链接:leetcode-cn.com/problemset/…