本篇主要是字符串相关的算法题,涉及的解决方案有栈、滑动窗口、哈希表等
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/…