文章目录
3. 无重复字符的最长子串

public static int lengthOfLongestSubstring(String s) {
int n = s.length(), ans = 0;
Map<Character, Integer> map = new HashMap<>();
for (int end = 0, start = 0; end < n; end++) {
char letter = s.charAt(end);
// 如果该字符已经被加入到了map中,更新start的位置
if (map.containsKey(letter)) {
start = Math.max(map.get(letter), start);
}
// 遍历一次算一次最大长度
ans = Math.max(ans, end - start + 1);
// 将遍历到字符存入map中,key为字母,value为字母下标 + 1
map.put(s.charAt(end), end + 1);
}
return ans;
}
5. 最长回文子串
public class Solution {
public static void main(String[] args) {
String str = "ababaabc";
System.out.println(longestPalindrome(str));
}
public static String longestPalindrome(String s) {
if (s == null || s.length() < 1) return "";
int start = 0, end = 0;
for (int i = 0; i < s.length(); i++) {
// 中心点是一个字符的时候:dbabc a
int len1 = expandAroundCenter(s, i, i);
// 中心点是两个字符的时候:cabbad bb
int len2 = expandAroundCenter(s, i, i + 1);
// 回文子串的长度
int len = Math.max(len1, len2);
// 本次循环中回文子串的长度大于上次回文串的长度,更新start和end的值
if (len > end - start) {
//回文子串的起始下标
start = i - (len - 1) / 2;
//回文子串的结束下标
end = i + len / 2;
}
}
// substring()方法返回的子字符串,左闭右开
return s.substring(start, end + 1);
}
/**
* 求扩展区的长度 左右闭
* left == right 的时候以一个字符串为中心开始比较
* left < right 的时候以两个字符串为中心开始比较(先比较这两个字符串是否相同)
* @param s
* @param left 起始下标
* @param right 结束下标
* @return
*/
private static int expandAroundCenter(String s, int left, int right) {
int L = left, R = right;
while (L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)) {
L--;
R++;
}
return R - L - 1;
}
}
6. Z 字形变换
class Solution {
public static void main(String[] args) {
String str = "LEETCOD";
String convert = convert(str, 3);
System.out.println(convert);
}
public static String convert(String s, int numRows) {
if (numRows < 2) {
return s;
}
List<StringBuilder> rows = new ArrayList<>();
// 为每一行创建一个StringBuilder对象来连接字符
for (int i = 0; i < numRows; i++) {
rows.add(new StringBuilder());
}
// i用来控制添加字母到哪一行
int i = 0;
// flag表示 从上到下 还是从下到上的添加过程
int flag = -1;
for(char c : s.toCharArray()) {
rows.get(i).append(c);
// 如果i = 0, 则从上到下的行顺序添加,此时flag = 1
// 如果i = numRows - 1,则从下到上的行顺序添加,flag = -1
if (i == 0 || i == numRows - 1) {
flag = - flag;
}
// 更新行号
i += flag;
}
StringBuilder res = new StringBuilder();
for (StringBuilder row : rows) {
res.append(row);
}
return res.toString();
}
}
14. 最长公共前缀
class Solution {
public static void main(String[] args) {
String[] strs = new String[]{"flower", "flow", "flight"};
System.out.println(longestCommonPrefix(strs));
}
public static String longestCommonPrefix(String[] strs) {
if (strs.length == 0 || strs[0].length() == 0) {
return "";
}
//假设第一个为最长的,用第一个一次去和后面的比
String ans = strs[0];
//从第二个字符串开始比较
for (int i = 1; i < strs.length; i++) {
int j = 0;
//比较字符串中的每个字符
for (; j < Math.min(ans.length(), strs[i].length()); j++) {
//如果有不相等的,ans保留相等的部分
if (ans.charAt(j) != strs[i].charAt(j)) {
if (j == 0) {
return "";
}
break;
}
}
ans = ans.substring(0, j);
}
return ans;
}
}
17. 电话号码的字母组合
/**
* 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
* 输入:"23"
* 输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]
*/
class Solution {
private static Map<String, String> phone = new HashMap<String, String>() {{
put("2", "abc");
put("3", "def");
put("4", "ghi");
put("5", "jkl");
put("6", "mno");
put("7", "pqrs");
put("8", "tuv");
put("9", "wxyz");
}};
private static List<String> output = new ArrayList<>();
public static void main(String[] args) {
Solution solution = new Solution();
String digits = "23";
solution.letterCombinations(digits);
System.out.println(Arrays.asList(output).toString());
}
public List<String> letterCombinations(String digits) {
if (digits.length() != 0)
backtrack("", digits);
return output;
}
public void backtrack(String combination, String next_digits) {
// 递归退出条件,如果数字字符串为空,将已经组合好的字符串加入到结果,然后返回
if (next_digits.length() == 0) {
output.add(combination);
}
// 如果数字字符串不为空
else {
// 从数字字符串前面截取一个数字 (2)
String digit = next_digits.substring(0, 1);
// 获取数字对应的字母字符串 (2 - abc)
String letters = phone.get(digit);
// 遍历字符串
for (int i = 0; i < letters.length(); i++) {
// 获取字符串中的每一个字母 (a)
String letter = phone.get(digit).substring(i, i + 1);
// 递归进行字母的组合 ("" + a, 3)
backtrack(combination + letter, next_digits.substring(1));
}
}
}
}
20. 有效的括号

方法一
class Solution {
public boolean isValid(String s) {
if (s == null || "".equals(s)) {
return true;
}
//用栈保存 (,[,{
Stack<Character> stack = new Stack<>();
//当遍历到 )时候就会去map中找对应的value,也就是(
//再用这个value和stack弹出的元素比较,如果相等则匹配上,不等则返回false
HashMap<Character, Character> map = new HashMap<>();
// key 是结束括号,value是开始括号
map.put(')', '(');
map.put(']', '[');
map.put('}', '{');
for (int i = 0; i < s.length(); i++) {
// 获取当前字符
char c = s.charAt(i);
//1、当前是开始括号 ( [ { 就放入栈中
if (!map.containsKey(c)) {
stack.add(c);
} else {
//2、当前是结束括号 ) ] } 就从栈中拿出一个
//如果栈为空,则还没有遍历到左括号(一上来就是右括号),不完整
if (stack.size() == 0) {
return false;
}
//如果栈中有左括号,就弹出一个
Character tmp = stack.pop();
//假设当前遍历到的字符是:],那么从map中取到的value就是:[
//如果栈顶的元素是 (,则不匹配返回false,否则继续
if (map.get(c) != tmp) {
return false;
}
}
}
//返回的时候还要判断栈是否为空
//如果输入的字符串是 (((,那么最后栈就不为空
return stack.isEmpty();
}
}

方法二
/**
* 只用栈实现
* 思路:碰到开始括号,栈中压入对应的结束括号
* 碰到结束括号,从栈中弹出一个看是否和当前括号一样
* @param s
* @return
*/
public boolean isValid2(String s) {
if (s.isEmpty())
return true;
Stack<Character> stack = new Stack<>();
for (char c : s.toCharArray()) {
if (c == '(')
stack.push(')');
else if (c == '{')
stack.push('}');
else if (c == '[')
stack.push(']');
else if (stack.empty() || c != stack.pop())
return false;
}
return stack.isEmpty();
}
22. 括号生成


//https://leetcode-cn.com/problems/generate-parentheses/solution/
public class Solution {
public List<String> generateParenthesis(int n) {
List<String> res = new ArrayList<>();
// 特判
if (n == 0) {
return res;
}
// 执行深度优先遍历,搜索可能的结果
dfs("", n, n, res);
return res;
}
/**
* @param curStr 当前递归得到的结果
* @param left 左括号还有几个可以使用
* @param right 右括号还有几个可以使用
* @param res 结果集
*/
private void dfs(String curStr, int left, int right, List<String> res) {
// 因为每一次尝试,都使用新的字符串变量,所以无需回溯
// 在递归终止的时候,直接把它添加到结果集即可,注意与「力扣」第 46 题、第 39 题区分
if (left == 0 && right == 0) {
res.add(curStr);
return;
}
// 剪枝(如图,左括号可以使用的个数严格大于右括号可以使用的个数,才剪枝,注意这个细节)
if (left > right) {
return;
}
if (left > 0) {
dfs(curStr + "(", left - 1, right, res);
}
if (right > 0) {
dfs(curStr + ")", left, right - 1, res);
}
}
}
43. 字符串相乘
方法一
class Solution {
public static void main(String[] args) {
String num1 = "123";
String num2 = "456";
System.out.println(multiply(num1, num2));
}
public static String multiply(String num1, String num2) {
if (num1.equals("0") || num2.equals("0")) {
return "0";
}
// 保存计算结果
String res = "0";
// num2 每一位 与 num1 相乘,字符串从尾到头依次截取
for (int i = num2.length() - 1; i >= 0; i--) {
int carry = 0;
// 保存 num2 第i位数字与 num1 相乘的结果
StringBuilder temp = new StringBuilder();
// num2 中除了个位外,其余位的结果补0,十位补一个0,依次类推
for (int j = 0; j < num2.length() - 1 - i; j++) {
temp.append(0);
}
// 截取num2的每一位:个位 -> 十位 -> 百位。。。
// 并将char类型转为int类型
int n2 = num2.charAt(i) - '0';
// num2 的第 i 位数字 n2 与 num1 相乘
for (int j = num1.length() - 1; j >= 0 || carry != 0; j--) {
// num1 的最高位与 n2 相乘之后可能有进位,此时 j=-1,carry为进位值
int n1 = j < 0 ? 0 : num1.charAt(j) - '0';
// n2 与 num1 的每一位乘之后的个位
int product = (n1 * n2 + carry) % 10;
temp.append(product);
// n2 与 num1 的每一位乘之后的进位
carry = (n1 * n2 + carry) / 10;
}
// 将当前结果与新计算的结果求和作为新的结果
res = addStrings(res, temp.reverse().toString());
}
return res;
}
/**
* 对两个字符串数字进行相加,返回字符串形式的和
*/
public static String addStrings(String num1, String num2) {
StringBuilder builder = new StringBuilder();
// 相加大于10的时候,进位的值
int carry = 0;
for (int i = num1.length() - 1, j = num2.length() - 1;
i >= 0 || j >= 0 || carry != 0;
i--, j--) {
int x = i < 0 ? 0 : num1.charAt(i) - '0';
int y = j < 0 ? 0 : num2.charAt(j) - '0';
// 相加大于10时,sum值为个位
int sum = (x + y + carry) % 10;
// 添加到和字符串
builder.append(sum);
// 相加大于10时,计算进位
carry = (x + y + carry) / 10;
}
return builder.reverse().toString();
}
}

方法二
public static 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--) {
// 从个位开始截取 num1中的每个数
int n1 = num1.charAt(i) - '0';
for (int j = num2.length() - 1; j >= 0; j--) {
// 从个位开始截取 num1中的每个数
int n2 = num2.charAt(j) - '0';
// 截取的两个数相乘 并加上之前的进位(之前相乘的积的第一位)
int sum = (res[i + j + 1] + n1 * n2);
// 当前所有积相加的第二位
res[i + j + 1] = sum % 10;
// 当前所有积相加的第一位,作为下一个积的个位
res[i + j] += sum / 10;
}
}
StringBuilder result = new StringBuilder();
for (int i = 0; i < res.length; i++) {
if (i == 0 && res[i] == 0) continue;
result.append(res[i]);
}
return result.toString();
}

93. 复原IP地址
/**
* ip格式:xxx.xxx.xxx.xxx
* 每一个部分大小在[0 255]
* 1.1.1.1 ~ 255.255.255.255 字符串最小长度为4,最大长度为12
* 每个部分可能是:1位 2位 3位
* 如果是两位开头不能是 0
*/
class Solution {
public List<String> restoreIpAddresses(String s) {
ArrayList<String> res = new ArrayList<>();
if (s.length() < 4 || s.length() > 12) return res;
dfs(res, s, "", 0);
return res;
}
/**
* 1. 定义
* @param res ip结果
* @param remainStr 剩余字符串
* @param ipStr 已经截取的ip字符串
* @param index 分成的部分(0,1,2,3)
*/
public void dfs(List<String> res, String remainStr, String ipStr, int index) {
// 3. 出口,下面两个判断不能换位置 (.1.1.1.1)
if (index == 4 && remainStr.length() == 0) res.add(ipStr.substring(1));
if (index == 4 || remainStr.length() == 0) return;
// 2. 拆解
// 一位数
dfs(res, remainStr.substring(1), ipStr + "." + remainStr.substring(0, 1), index + 1);
// 两位数
if (remainStr.charAt(0) != '0' && remainStr.length() > 1) {
dfs(res, remainStr.substring(2), ipStr + "." + remainStr.substring(0, 2), index + 1);
// 三位数
if (remainStr.length() > 2 && Integer.valueOf(remainStr.substring(0, 3)) <= 255) {
dfs(res, remainStr.substring(3), ipStr + "." + remainStr.substring(0, 3), index + 1);
}
}
}
}
678. 有效的括号字符串

class Solution {
public static void main(String[] args) {
String str1 = "**)))";
String str2 = "((()*";
System.out.println(checkValidString(str2));
}
public static boolean checkValidString(String s) {
int minOp = 0; // 必须被匹配的最小左括号数
int maxOp = 0; // 可能的最大左括号数
for (char c : s.toCharArray()) {
// 如果是左括号,则强制匹配的左括号数加一
if (c == '(') minOp++; // (
// 不是左括号
else minOp--;
// 如果是左括号或者*,可能的最大左括号数加一
if (c != ')') maxOp++; // ( or *
// 右括号
else maxOp--; // )
// maxOp < 0 则右括号已经比左括号多了
if (maxOp < 0) return false;
minOp = Math.max(0, minOp);
}
return minOp == 0;
}
}

758. 字符串中的加粗单词

方法一
public static String boldWords(String[] words, String S) {
// 字典里面每个单词的最大长度,根据题意最大为 10
int kMaxWordLen = 10;
int n = S.length();
HashSet<String> set = new HashSet<>();
// 标记数组:用于标记S中那些位置的字符应该被加粗,加粗位置的值置 1
int[] bold = new int[n];
for (String word : words) {
set.add(word);
}
// 遍历字符串中的每个字符
for (int i = 0; i < n; i++) {
// 以当前字符开头的子串,子串的长度依次递减
// 如果已经遍历到 S 的快结束位置了,子串的长度不够 10,最大应该为 n - i
for (int l = Math.min(n - i, kMaxWordLen); l >= 1; l--) {
// 在 S中截取从 i开始共 l个子串
if (set.contains(S.substring(i, i + l))) {
// 如果在set集合中,就把标记数组的 i 到 i+l 位置赋值为 1
for (int j = 0; j < l; j++) {
bold[j + i] = 1;
}
// 因为是先比较长的,所以短的肯定也在字典里面
break;
}
}
}
StringBuilder res = new StringBuilder();
for (int i = 0; i < n; i++) {
// 当前位置的需要加粗,并且以 [...0,1...]开始 或者 [1,...] 开始就加上<b>
if (bold[i] == 1 && (i == 0 || bold[i - 1] != 1)) res.append("<b>");
res.append(S.charAt(i));
// 以[...,1,0] 或者 [...,1]结尾就加上</b>
if (bold[i] == 1 && (i == n - 1 || bold[i + 1] != 1)) res.append("</b>");
}
return res.toString();
}
方法二
public static String boldWords(String[] words, String S) {
int[] s = new int[S.length() + 1];
// 遍历字典
for (String w : words) {
int i = 0;
// w 从 S 的 i 位置开始第一次在 S 中出现的索引,如果从 i 位置开始没有找到则返回 -1
while ((i = S.indexOf(w, i)) >= 0) {
s[i]++;
s[i + w.length()]--;
i++;
}
}
StringBuilder res = new StringBuilder();
int pre = 0, sum = 0;
for (int i = 0; i <= S.length(); i++) {
sum += s[i];
if (sum > 0 && pre == 0) res.append("<b>");
if (sum == 0 && pre > 0) res.append("</b>");
if (i < S.length()) res.append(S.charAt(i));
pre = sum;
}
return res.toString();
}
1249. 移除无效的括号

class Solution {
public static void main(String[] args) {
String str = "a(b)c(d)e)";
System.out.println(minRemoveToMakeValid(str));
}
public static String minRemoveToMakeValid(String s) {
if (s == null || s.length() == 0) return "";
//
int count = 0;
StringBuilder sb = new StringBuilder();
// 从左往右,去掉多余的右括号,左括号可能多余
for (char c : s.toCharArray()) {
if (c == '(') {
count++;
} else if (c == ')') {
// 左括号数量为 0,跳过当前右括号
if (count == 0) {
continue;
}
count--;
}
sb.append(c);
}
// 从右往左 count >= 0
StringBuilder res = new StringBuilder();
for (int i = sb.length() - 1; i >= 0 ; i--) {
// 遍历到左括号,如果未匹配的左括号数量不为 0,跳过当前左括号
if (sb.charAt(i) == '(' && count-- > 0) {
continue;
}
res.append(sb.charAt(i));
}
return res.reverse().toString();
}
}
删除多余的空格
输入:__a_b__c___d
输出:a_b_c_d
public String removeExtraSpace(String s) {
StringBuilder res = new StringBuilder();
for (int i = 0; i < s.length(); ) {
// 如果当前字符是一个空格,进入这个if,表明至少有一个空格
if (s.charAt(i) == ' ') {
// 如果不是字符串的第一个字符,为 true
boolean isntBegin = i != 0;
// 如果是空格就继续判断下一个,直到不是空格
while (i < s.length() && s.charAt(i) == ' ') {
i++;
}
// 当前判断的不是第一个字符,在结果上追加一个空格
// 经过上面的判断可能跳过很多空格,可能没有跳过(只有一个空格)
if (isntBegin && i < s.length()) {
res.append(' ');
}
}
// 如果当前字符不是空格,添加到结果中,判断下一个是否是空格
// 如果遇到空格就跳出循环,由上面的if判断来消除多余的空格
while (i < s.length() && s.charAt(i) != ' ') {
res.append(s.charAt(i));
i++;
}
}
return res.toString();
}
690

被折叠的 条评论
为什么被折叠?



