📌牛客Top100热题宝典--第4节
07 字符串
BM83 字符串变形
进阶:空间复杂度 O(n), 时间复杂度 O(n)
// 65 A
// 97 a
String[] split(String regex, int limit)
:根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中。
参数2: limit匹配限制
limit的值 含义
大于0 最多匹配limit-1次,次数到了,最后面的作为一个整体返回,数组长度不会超过limit
等于0 一直匹配到结束,但是如果后面全是空串,则丢弃
小于0 一直匹配到结束,不丢弃空串
import java.util.*;
// 65 A
// 97 a
public class Solution {
public String trans(String s, int n) {
StringBuilder res = new StringBuilder();
// 注意第二个参数的使用!!
String[] wordList = s.split(" ",-1);
int m = wordList.length;
for (int i = m - 1; i >= 0; i--) {
System.out.print("***" + wordList[i]);
char[] tmp = wordList[i].toCharArray();
for (int j = 0; j < tmp.length; j++) {
tmp[j] = trans(tmp[j]);
}
res.append(new String(tmp));
res.append(" ");
}
return res.substring(0, n).toString();
}
public char trans(char ch) {
if (ch >= 'a' && ch <= 'z') {
return (char)(ch - ('a' - 'A' ));
} else
return (char)(ch + ('a' - 'A'));
}
}
BM84 最长公共前缀
思路:纵向扫描
进阶:空间复杂度 O(n),时间复杂度 O(n)
import java.util.*;
public class Solution {
public String longestCommonPrefix (String[] strs) {
if (strs.length == 0 || strs == null)
return "";
//位置不能颠倒!!否则下标溢出!!
int rows = strs.length,cols = strs[0].length();
for (int j = 0; j < cols ; j++) {
char prefix = strs[0].charAt(j);
for (int i = 1; i < rows ; i++) {
if (strs[i].length() == j || strs[i].charAt(j) != prefix)
return strs[0].substring(0, j);
}
}
return strs[0];
}
}
BM85 验证IP地址
【力扣!】
BM86 大数加法
import java.util.*;
public class Solution {
public String solve (String s, String t) {
StringBuilder res=new StringBuilder();
int i=s.length()-1,j=t.length()-1;
int carry=0,bit=0;
while(i>=0 || j>=0){
int sum=0;
// 字符转成整数
// 方式一 Character.getNumericValue(numChar))
// 方式二
int n1=i>=0?s.charAt(i)-'0':0;
int n2=j>=0?t.charAt(j)-'0':0;
sum=n1+n2+carry;
carry=sum/10;
bit=sum%10;
res.append(bit);
i--;
j--;
}
// 处理1+9样例
if(carry==1)
res.append("1");
return res.reverse().toString();
}
}
08 双指针
3. 无重复字符的最长子串
方式:滑动窗口
其实就是一个窗口,比如例题中的 abcabcbb,进入这个窗口为 abc 满足题目要求,当再进入 a,窗口变成了 abca,这时候不满足要求。所以,我们要移动这个窗口!
只要把窗口的左边的元素移出就行了,直到满足题目要求!一直维持这样的窗口,找出窗口出现最长的长度时候,求出解!
时间复杂度:O(n)
class Solution {
public int lengthOfLongestSubstring(String s) {
HashMap<Character, Integer> map = new HashMap<>();
int n = s.length();
int left = -1;
int right = 0;
int res = 0;
while (right < n) {
char ch = s.charAt(right);
if (map.containsKey(ch)) {
left = Math.max(left, map.get(ch));
}
map.put(ch, right);
res = Math.max(res, right - left);
right++;
}
return res;
}
}
BM87 合并两个有序的数组
import java.util.*;
public class Solution {
public void merge(int A[], int m, int B[], int n) {
int[] res = new int[m + n];
int i=0,j=0,p=0;
while(i<m && j<n){
res[p++]=A[i]<=B[j]?A[i++]:B[j++];
}
while(i<m){
res[p++]=A[i++];
}
while(j<n){
res[p++]=B[j++];
}
for(int r=0;r<p;r++){
A[r]=res[r];
}
}
}
BM88 判断是否为回文字符串
import java.util.*;
// 2,使用StringBuffer
public class Solution {
public boolean judge (String str) {
String rev=new StringBuilder(str).reverse().toString();
return str.equals(rev);
}
}
BM89 合并区间
思路:
先按照区间起始位置排序,如果intervals[i][1]>=intervals[i+1][0],那么循环合并两个区间。
import java.util.*;
// 难点:分析intervals结构,根据结构进行解题!!
public class Solution {
public ArrayList<Interval> merge(ArrayList<Interval> intervals) {
ArrayList<Interval> res = new ArrayList<>();
Collections.sort(intervals, (a, b)->a.start - b.start);
int len=intervals.size();
int i=0;
while(i<len){
int left=intervals.get(i).start;
int right=intervals.get(i).end;
while(i+1<len && right>=intervals.get(i+1).start){
right=Math.max(right,intervals.get(i+1).end);
i++;
}
res.add(new Interval(left,right));
i++;
}
return res;
}
}
class Solution {
public static int[][] merge(int[][] intervals) {
int n = intervals.length;
List<int[]> res = new ArrayList<>();
Arrays.sort(intervals, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] - o2[0];
}
});
int i = 0;
while (i < n) {
int left = intervals[i][0];
int right = intervals[i][1];
while (i+1<n && intervals[i + 1][0] <= right) {
right = Math.max(right, intervals[i + 1][1]);
i++;
}
res.add(new int[]{left, right});
i++;
}
//二维list转换成二维数组
return res.toArray(new int[res.size()][]);
}
}
BM90 最小覆盖子串 【难!!!】
要求: 时间复杂度 O(n)
思路:滑动窗口
【包含小写字母和大写字母!!】
当窗口内的所有字符不能覆盖t的时候,要扩大窗口,也就是right往右移。
当窗口内的所有字符可以覆盖t的时候,记录窗口的起始位置以及窗口的长度,然后缩小窗口(因为这里求的是能覆盖的最小子串),left往右移。如果缩小的窗口还能覆盖t,保存长度最小的窗口即可。
重复上面的操作,直到窗口的右边不能再移动为止。
import java.util.*;
public class Solution {
public String minWindow (String S, String T) {
int m = S.length(), n = T.length();
//map确保窗口是否覆盖全部T中的字符
HashMap<Character, Integer> map = new HashMap<>();
//把T中的字符全部放到map中
for (char ch : T.toCharArray())
map.put(ch, map.getOrDefault(ch, 0) + 1);
//窗口的左边界和右边界
int left = 0, right = 0;
//窗口开始位置
int start = 0;
//窗口的长度
int winLength = Integer.MAX_VALUE;
while (right < m) {
char rightChar = S.charAt(right);
// 如果右指针扫描的字符存在于map中,就减1
if (map.containsKey(rightChar)) {
map.put(rightChar, map.get(rightChar) - 1);
}
right++;
// 检查窗口是否把t中字符全部覆盖了,如果覆盖了,要移动窗口的左边界
while (check(map)) {
// 如果现在窗口比之前保存的还要小,就更新窗口的长度
// 以及窗口的起始位置
if (right - left < winLength) {
winLength = right - left;
start = left;
}
// 移除窗口最左边的元素,也就是缩小窗口
char leftChar = S.charAt(left);
if (map.containsKey(leftChar)) {
// 表示窗口中欠缺该字符,没有包含该字符!!
map.put(leftChar, map.get(leftChar) + 1);
}
left++;
}
}
return winLength != Integer.MAX_VALUE ? S.substring(start,
start + winLength) : "";
}
// 检查窗口是否把字符串t中的所有字符都覆盖了,如果map中所有
// value的值都不大于0,则表示全部覆盖
public boolean check(HashMap<Character, Integer> map) {
for (int value : map.values()) {
if (value > 0) return false;
}
return true;
}
}
getOrDefault()
方法获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值。
getOrDefault()
方法的语法为:
hashmap.getOrDefault(Object key, V defaultValue)
438. 找到字符串中所有字母异位词
方式一:滑动窗口(左右指针)
【仅包含小写字母!!】
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> res = new ArrayList<>();
int[] chs = new int[26];
int[] chp = new int[26];
int n = s.length();
int m = p.length();
for (int i = 0; i < p.length(); i++) {
chp[p.charAt(i) - 'a']++;
}
for (int left = 0, right = 0; right < n; right++) {
char rightChar = s.charAt(right);
chs[rightChar - 'a']++;
if (right - left + 1 > m) {
char leftChar = s.charAt(left);
chs[leftChar - 'a']--;
left++;
}
if (check(chs, chp)) res.add(left);
}
return res;
}
private boolean check(int[] chs, int[] chp) {
for (int i = 0; i < 26; i++) {
if (chs[i] != chp[i])
return false;
}
return true;
}
}
BM92 最长无重复子数组
【可能有多个无重复数组,选择长度最长的一个数组!!】
使用集合set来代替队列,用两个指针,一个left一个right,如果有重复的就把left指向的给移除(left相当于队首,right相当于队尾)
import java.util.*;
public class Solution {
public int maxLength (int[] arr) {
int res = 0;
HashSet<Integer> set = new HashSet<>();
int left = 0, right = 0;
int n = arr.length;
while (right < n) {
if (!set.contains(arr[right])) {
set.add(arr[right++]);
res = Math.max(res, set.size());
} else {
set.remove(arr[left++]);
}
}
return res;
}
}
BM93 盛水最多的容器
双指针
public class Solution {
public int maxArea (int[] height) {
int n = height.length;
int left = 0;
int right = n - 1;
int res = 0;
//此处不能相等,因为面积不能为0
while (left < right) {
if (height[left] < height[right]) {
res = Math.max(res, (right - left) * height[left]);
left++;
} else {
res = Math.max(res, (right - left) * height[right]);
right--;
}
}
return res;
}
}
BM94 接雨水问题
import java.util.*;
public class Solution {
public long maxWater (int[] arr) {
int n = arr.length;
long res = 0;
int[] lMax = new int[n];
int[] rMax = new int[n];
lMax[0]=arr[0];
rMax[n-1]=arr[n-1];
for (int i = 1; i < n; i++) {
lMax[i] = Math.max(arr[i], lMax[i - 1]);
}
for (int i = n - 2; i >=0; i--) {
rMax[i] = Math.max(arr[i], rMax[i+1]);
}
for (int i = 0; i < n; i++) {
res+=Math.min(lMax[i], rMax[i]) - arr[i];
}
return res;
}
}
283. 移动零
必须在不复制数组的情况下原地对数组进行操作。
方式:双指针
left指向0的下一个位置,right用于遍历数组,交换数字即可
class Solution {
public void moveZeroes(int[] nums) {
int n = nums.length;
int left = 0;
int right = 0;
while (right<n) {
if (nums[right] != 0) {
swap(nums, left, right);
left++;
}
right++;
}
}
private void swap(int[] nums, int left, int right) {
int tmp = nums[left];
nums[left] = nums[right];
nums[right] = tmp;
}
}
581. 最短无序连续子数组
581. 最短无序连续子数组
一般来说题目如果暗示你可以O(n)对序列进行操作的话,就可以考虑一下双指针的解法。这道题的双指针其实不是很明显,需要一定的思考。
思路:数组分成三段,第一段中最大元素小于中间段的最小元素,中间段最大元素小于第三段最小元素,且首段和尾段的元素是有序的。找到对应的边界即可。
- 从左往右遍历,边遍历边确定遍历区间的最大值,如果当前值小于最大值,那么这个值肯定需要排序,这样就可以确定右边界。
- 从右往左遍历,边遍历边确定遍历区间的最小值,如果当前值大于最小值,那么这个值肯定需要排序,这样就可以确定左边界。
- 可以使用一次遍历直接进行从左往右和从右往左遍历。
class Solution {
public int findUnsortedSubarray(int[] nums) {
int n = nums.length;
// 最后一个逆序的位置!!
int left = -1;
int right = -1;
int maxn = Integer.MIN_VALUE;
int minn = Integer.MAX_VALUE;
for (int i = 0; i < n; i++) {
if (nums[i] >= maxn) {
maxn = nums[i];
} else {
right = i;
}
if (nums[n - 1 - i] <= minn)
minn = nums[n - 1 - i];
else
left = n - 1 - i;
}
return right == -1 ? 0 : right - left + 1;
}
}
整理不易🚀🚀,关注和收藏后拿走📌📌欢迎留言🧐👋📣
欢迎专注我的公众号AdaCoding 和 Github:AdaCoding123