【剑指offer】数据结构——字符串

news2025/1/4 17:22:51

在这里插入图片描述

目录

  • 数据结构——字符串
    • 直接解
      • 【剑指offer】05. 替换空格
      • 【剑指offer】17. 打印从1到最大的n位数
      • 【剑指offer】20. 表示数值的字符串
      • 【剑指offer】37. 序列化二叉树
      • 【剑指offer】50. 第一个只出现一次的字符
      • 【剑指offer】58. 翻转单词顺序
      • 【剑指offer】58.2 左旋转字符串
      • 【剑指offer】67. 把字符串转换成整数
    • 特殊解——动态规划
      • 【剑指offer】19. 正则表达式匹配
    • 特殊解——回溯搜索
      • 【剑指offer】38. 字符串的排列
    • 特殊解——双指针
      • 【剑指offer】48. 最长不含重复字符的子字符串

数据结构——字符串

直接解

【剑指offer】05. 替换空格

题目描述

//05. 替换空格
//请实现一个函数,把字符串 s 中的每个空格替换成"%20"。s

题解

// 遍历拼接法
// 遇到空格就加%20,空格左右重新拼接。
// 时间复杂度:5 ms , 在所有 Java 提交中击败了 5.50% 的用户
// 空间复杂度:38.1 MB , 在所有 Java 提交中击败了 5.00% 的用户
class Solution {
	public String replaceSpace(String s) {
        String ns = s;
        for (int i=0; i<=ns.length() - 1; i++) {
            if (ns.charAt(i) == ' ') {
                ns = ns.substring(0,i) + "%20" + ns.substring(i+1, ns.length());
            }
        }
        return ns;
	}
}



// 利用StringBuilder一个个拼接
class Solution {
	public String replaceSpace(String s) {
        StringBuilder res = new StringBuilder();
        int start = 0;
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == ' ') {
                res.append(s.substring(start, i) + "%20");
                start = i + 1;
            }
        }
        if (start != s.length())
            res.append(s.substring(start, s.length()));
        return res.toString();
    }
}



// 一个个遍历,遇到空格加%20
// 时间复杂度O(N):0 ms , 在所有 Java 提交中击败了 100.00% 的用户
// 空间复杂度O(N):36 MB , 在所有 Java 提交中击败了 98.40% 的用户
class Solution {
	public String replaceSpace(String s) {
		StringBuilder sb = new StringBuilder();
		for (int i=0; i < s.length(); i++) {
			char c = s.charAt(i); // 遍历所有的字符
			if (c == " ") {  // 是空格就放%20
				sb.append("%20");
			}
			else {  // 不是空格就直接放
				sb.append(c);
			}
		}
		return sb.toString();
	}
}



// 调包法
// 时间复杂度:0 ms , 在所有 Java 提交中击败了 100.00%的用户
// 空间复杂度:36.3 MB , 在所有 Java 提交中击败了90.21% 的用户
class Solution {
	public String replaceSpace(String s) {
        return s.replace(" ", "%20");
	}
}



// 双指针法
// 时间复杂度:1 ms , 在所有 Java 提交中击败了 26.74% 的用户
// 空间复杂度:36.3 MB , 在所有 Java 提交中击败了 91.47%的用户
class Solution {
	public String replaceSpace(String s) {
        StringBuffer ns = new StringBuffer(s);
        int p1 = ns.length() - 1;
        for (int i = 0; i <= p1; i++) {
            if (ns.charAt(i) == ' ') {
                ns.append("  ");
            }
        }
        System.out.println(ns);
        int p2 = ns.length() - 1; // 新字符串的末位
        while (p1 >= 0 && p2 >= p1) {
            char c = ns.charAt(p1--);
            if (c == ' ') {
                ns.setCharAt(p2--, '0');
                ns.setCharAt(p2--, '2');
                ns.setCharAt(p2--, '%');
                System.out.println(ns);
            }
            else {
                ns.setCharAt(p2--, c);
                System.out.println(ns);
            }
        }
        System.out.println(ns);
        return ns.toString();
    }
}


【剑指offer】17. 打印从1到最大的n位数

题目描述

在这里插入图片描述

// 力扣
// 输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,
// 则打印出 1、2、3 一直到最大的 3 位数 999。

题解

// 直接法(不推荐,毫无意义)

// 力扣
// 执行用时:1 ms, 在所有 Java 提交中击败了100.00%的用户
// 内存消耗:46.3 MB, 在所有 Java 提交中击败了90.56%的用户
class Solution {
	public int[] printNumber(int n) {
		int end = (int) Math.pow(10, n) - 1;
		int[] res = new int[end + 1];
		for (int i = 0; i < end; i++) {
			res[i] = i + 1;
		}
		return res;
	}
}

 打印形式 

class Solution {
    public static void printNumbers(int n) {
        StringBuilder str = new StringBuilder();
        // 初始化,n为多少就拓展为n位的0
        // 如n=3,则str为'000'
        for (int i = 0; i < n; i++) {
            str.append('0');
        }
        while (!addOne(str)) {  // 如果+1之后没有进位
            int index = 0;  // index初始化为0
            // index从左到右(高位到低位)遍历str的每一位字符,直到不为0的字符值
            // 如果index超过str长度边界(不可能,所以这句可以不要),则跳出while
            // 或如果index遍历的str字符不为0,则跳出while(主要用这句控制)
            while (index < str.length() && str.charAt(index) == '0') {
                index++;
            }
            // 打印str,index的位置为str不为0的位置
            // substring(index)会保留str在index位置以后的字符
            // 所以就能够保证非n位数前面的0被去除,比如原来是02,只打印2
            System.out.println(str.toString().substring(index));
        }
    }

    // 执行+1操作的函数,返回进位标识符,
    // 如果+1进位,返回true,否则返回false
    public static boolean addOne(StringBuilder str) {
        // 进位标识符,进位true,不进位false
        boolean isOverflow = false;
        // 从字符串str右边开始往左遍历,即从低位往高位遍历
        // 当前遍历位索引记为i
        // 1:如果第i位字符的值+1之后不进位,则循环结束,函数返回false
        // 2:如果第i位字符的值+1之后进位,则i左移,循环会遍历str高位,令高位执行+1操作。
        for (int i = str.length() - 1; i >= 0; i--) {
            char s = (char) (str.charAt(i) + 1);  // 第i位字符值+1,记为字符s
            if (s > '9') {  // 如果第i为字符值+1之后大于9,需要进位
                str.replace(i, i + 1, "0");  // 进位后,str中第i位设为0
                if (i == 0)
                    isOverflow = true;  // 此时将进位符isOverflow设为true,表示需要进位
            }  // 进位后,for循环会继续遍历str的高位,将高位的字符值+1,完成进位
            else {  // 如果s <= '9'
                // 不进位,将第i位的字符值替换为已经+1的s
                str.replace(i, i + 1, String.valueOf(s));
                break;  // 终止当前for循环(也就是不继续遍历字符串str的高位)
            }
        }
        return isOverflow;  // 函数结束时返回进位标识符
    }
}

/ 数组形式  

// 执行用时:20 ms, 在所有 Java 提交中击败了5.02%的用户
// 内存消耗:46.2 MB, 在所有 Java 提交中击败了93.47%的用户
class Solution {
	int[] res;
    int i = 0;

	public int[] printNumbers(int n) {
        StringBuilder str = new StringBuilder();
        for (int i = 0; i < n; i++) {
            str.append('0');
        }
		res = new int[(int) Math.pow(10, n) - 1];
        while (!addOne(str)) {
            int index = 0;
            while (index < str.length() && str.charAt(index) == '0') {
                index++;
            }
			// Integer.parseInt将StringBuilder中的字符串转为整数
            res[i] = Integer.parseInt(str.substring(index));
            i++;
        }
		return res;
    }

    public boolean addOne(StringBuilder str) {
        boolean isOverflow = false;
        for (int i = str.length() - 1; i >= 0; i--) {
            char s = (char) (str.charAt(i) + 1);
            if (s > '9') { 
                str.replace(i, i + 1, "0"); 
                if (i == 0)
                    isOverflow = true;  
            }  
            else { 
                str.replace(i, i + 1, String.valueOf(s));
                break;  
            }
        }
        return isOverflow; 
    }
}



【剑指offer】20. 表示数值的字符串

题目描述

在这里插入图片描述

// 力扣 & 牛客
// 请实现一个函数用来判断字符串是否表示数值(包括整数和
// 小数)。例如,字符串"+100"、"5e2"、"-123"、"3.1416"、
// "-1E-16"、"0123"都表示数值,但"12e"、"1a3.14"、"1.2.3
// "、"+-5"及"12e+5.4"都不是。

// 建议做牛客的题,这题在力扣里的测试用例会带空格,非常智障

题解

// 条件判断 /

// 牛客
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param str string字符串 
     * @return bool布尔型
     */
    public boolean isNumeric (String str) {
        if(str == null || str.length() == 0) return false;
        
        char[] char_arr = str.toCharArray();
        int len = char_arr.length;

		boolean existE = false;
		int existSign = 0;
		boolean existSignM = false;
		boolean existPoint = false;
        boolean existNum = false;
		// 遍历str元素索引记为i
		for (int i = 0; i < len; i++) { 
			if (char_arr[i] == 'e' || char_arr[i] == 'E') {
                if (!existNum) return false;  // E e先于数字出现,false
				if (existE) return false;  // E e出现过,false 
				if (i == len - 1) return false;  // 首尾不能有E e
				existE = true;
			}
			else if (char_arr[i] == '+' || char_arr[i] == '-') {
                if (i == len - 1) return false;  // 尾不能有+ -
				if (i != 0 && char_arr[i - 1] != 'E' && char_arr[i - 1] != 'e') {
					return false;  // 如果+-号前面没有E e,false
				}
			}
			else if (char_arr[i] == '.') {
				if (existPoint) return false;  // 小数点出现两次,false
                if (existE) return false;  // 小数点在E e之前出现,false
				if (i == 0 || i == len - 1) return false;  // 小数点在头尾出现,false
				existPoint = true;
			}
			else if (char_arr[i] <= '9' && char_arr[i] >= '0') {
				existNum = true;
			}
            else {
                return false;
            }
		}
		return existNum;
    }
}
/ 正则表达式 
// 牛客
// 运行时间:14ms
// 占用内存:9892k

// []  : 字符集合
// ()  : 分组
// ?   : 重复 0 ~ 1 次
// +   : 重复 1 ~ n 次
// *   : 重复 0 ~ n 次
// .   : 任意字符
// \\. : 转义后的 .
// \\d : 数字

public class Solution {
    public boolean isNumeric(char[] str) {
		if (str == null || str.length == 0)
			return false;
		return new String(str).matches("[+-]?\\d*(\\.\\d+)?([eE][+-]?\\d+)?");
    }
}



【剑指offer】37. 序列化二叉树

题目描述

在这里插入图片描述

在这里插入图片描述

// 37. 序列化二叉树

// 请实现两个函数,分别用来序列化和反序列化二叉树。
// 你可以将以下二叉树:

//     1
//    / \
//   2   3
//      / \
//     4   5

// 序列化为 "[1,2,3,null,null,4,5]"



// 牛客
// 请实现两个函数,分别用来序列化和反序列化二叉树

// 二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格
// 式保存为字符串,从而使得内存中建立
// 起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序
// 的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时
// 通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(valu
// e!)。

// 二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str
// ,重构二叉树。

// 例如,我们可以把一个只有根节点为1的二叉树序列化为"1,",然后通过自己
// 的函数来解析回这个二叉树

题解

// 这题很难啊,
// 序列化是为了能够完整地保存树的结构信息,这个信息最后可以为
// 反序列化而服务,即根据这个序列化信息完整的还原这个二叉树。
// 按照题意,序列化使用层序遍历的同时需要将null也保存下来。
// 力扣
// 执行用时:23 ms, 在所有 Java 提交中击败了62.32%的用户
// 内存消耗:40.7 MB, 在所有 Java 提交中击败了47.02%的用户
import java.util.LinkedList;
public class Codec {
	
    // 序列化
    public String serialize(TreeNode root) {
		if (root == null)  // 若树为null直接返回"[]"
			return "[]";
		// 创建StringBuilder用于存string结构信息
        StringBuilder res = new StringBuilder();
		// 创建层序遍历要用的队列(用LinkedList模拟)
        LinkedList<TreeNode> q = new LinkedList<>();
        res.append("[");
        q.add(root);
        while(!q.isEmpty()) {
            TreeNode node = q.remove();  // 结点出队即为node
			// 如果遍历当前的node非空,将node.val和逗号","存入res
			// 并将node的左右子结点入队(不管是不是null,因为null我们也要记录)
			if (node != null) {
				res.append(node.val + ",");
				q.add(node.left);
				q.add(node.right);
			}
			else 
				res.append("null,");  // 如果是null,把"null,"存入
        }
		// 存完res末尾会多一个逗号,删掉
		res.deleteCharAt(res.length() - 1);  
        res.append("]");
        // System.out.println(res.toString());
        return res.toString();  // 转String
    }

    // 反序列化
    public TreeNode deserialize(String data) {
		if (data.equals("[]"))  // 如果string是"[]",说明树是null
			return null;
		// 先掐头去尾把中括号"[]"去掉,然后将data中的字符按照逗号分隔
		// 得到字符组vals
		String[] vals = data.substring(1, data.length() - 1).split(",");
		// parseInt将字符串参数解析为有符号的十进制数,即为结点值,
		// 用结点值新建结点作为遍历的第一个点
        TreeNode res = new TreeNode(Integer.parseInt(vals[0]));
		// 初始化队列q,用于构建二叉树,构建过程类似层序遍历
        LinkedList<TreeNode> q = new LinkedList<>();
		q.add(res);
		int i = 1;  // 遍历字符组vals的索引指针i
		// 若q为空(vals中的有效结点遍历完),循环结束
		while (!q.isEmpty()) {
			TreeNode node = q.remove();  // 结点出队
			// 如果vals[i]元素不是null
			if (!vals[i].equals("null")) {
				// 解析结点值新建结点,使node.left指向该结点
				node.left = new TreeNode(Integer.parseInt(vals[i]));
				q.add(node.left);  // 入队
			}
			i++;  // 指针右移
			// 如果下一个vals[i]元素不是null
			if (!vals[i].equals("null")) {
				// 同上
				node.right = new TreeNode(Integer.parseInt(vals[i]));
				q.add(node.right);
			}
			i++;  // 指针右移
		}
		return res;  // 最后返回树
    }
}



// 牛客
// 运行时间:18ms
// 占用内存:10032k
import java.util.LinkedList;
public class Solution {
    String Serialize(TreeNode root) {
        if (root == null)
            return "[]";
        StringBuilder res = new StringBuilder();
        LinkedList<TreeNode> q = new LinkedList<>();
        res.append("[");
        q.add(root);
        while (!q.isEmpty()) {
            TreeNode node = q.remove();
            if (node != null) {
                res.append(node.val + ",");
                q.add(node.left);
                q.add(node.right);
            }
            else
                res.append("null,");
        }
        res.append("]");
        return res.toString();
  }
    TreeNode Deserialize(String str) {
       if (str.equals("[]"))
           return null;
        String[] vals = str.substring(1, str.length() - 1).split(",");
        LinkedList<TreeNode> q = new LinkedList<>();
        TreeNode res = new TreeNode(Integer.parseInt(vals[0]));
        q.add(res);
        int i = 1;
        while (!q.isEmpty()) {
            TreeNode node = q.remove();
            if (!vals[i].equals("null")) {
                node.left = new TreeNode(Integer.parseInt(vals[i]));
                q.add(node.left);
            }
            i++;
            if (!vals[i].equals("null")) {
                node.right = new TreeNode(Integer.parseInt(vals[i]));
                q.add(node.right);
            }
            i++;
        }
        
        return res;
  }
}


【剑指offer】50. 第一个只出现一次的字符

题目描述
在这里插入图片描述
在这里插入图片描述


// 力扣
// 在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个
// 单空格。 s 只包含小写字母。

// 牛客
// 在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个
// 只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大
// 小写).(从0开始计数)

题解

/ 出现次数记录 /

// 力扣
// 执行用时:37 ms, 在所有 Java 提交中击败了16.73%的用户
// 内存消耗:38.9 MB, 在所有 Java 提交中击败了30.90%的用户
import java.util.HashMap;
class Solution {
    public char firstUniqChar(String s) {
        char[] strs = s.toCharArray();
		// 构建HashMap用于存储
		// key:strs中出现的字符,value:字符出现次数
        HashMap<Character, Integer> map = new HashMap<Character, Integer>();
        for (char str: strs) {
            if (!map.containsKey(str))
				map.put(str, 1);  // 没出现过,置1
            else 
                map.put(str, map.get(str)+1);  // 出现过,value+1
        }
		for (char key: strs) {
			if (map.get(key) == 1)  // 按照strs的顺序,逐个检查map对应的value
				return key;  // 第一个出现1的,返回字符
		}
        return ' ';
    }
}


// 牛客
// 运行时间:25ms,超过77.83%用Java提交的代码
// 占用内存:10112KB,超过9.32%用Java提交的代码
import java.util.HashMap;
public class Solution {
    public int FirstNotRepeatingChar(String str) {
        HashMap<Character, Integer> map = new HashMap<>();
        char[] strs = str.toCharArray();
        for (char s: strs) {
            if (!map.containsKey(s))
                map.put(s, 1);
            else {
                map.put(s, map.get(s) + 1);
            }
        }
        for (int i = 0; i < strs.length; i++) {
            if (map.get(strs[i]) == 1)
                return i;
        }
        return -1;
    }
}
// 出现状态记录 ///


// 力扣
import java.util.HashMap;
class Solution {
    public char firstUniqChar(String s) {
		// 将s转为字符组char[] strs
        char[] strs = s.toCharArray();
		// 构建HashMap用于存储
		// key:strs中出现的字符,value:字符是否出现次数小于2次(是则true)
        HashMap<Character, Boolean> map = new HashMap<Character, Boolean>();
        for (char str: strs) {
            if (!map.containsKey(str))  // 如果遍历字符str是第1次出现
				map.put(str, true);  // 将str对应的value置为true
            else 
                map.put(str, false);  // 如果出现次数大于等于2次,false
        }
		for (char key: strs) {
			if (map.get(key))  // 按照strs的顺序,逐个检查map对应的value
				return key;  // 第一个出现true的,返回字符
		}
        return ' ';
    }
}



// 牛客
// 运行时间:28ms,超过69.39%用Java提交的代码
// 占用内存:10116KB,超过9.24%用Java提交的代码
import java.util.HashMap;
public class Solution {
    public int FirstNotRepeatingChar(String str) {
        HashMap<Character, Boolean> map = new HashMap<Character, Boolean>();
        char[] strs = str.toCharArray();
        for (char s: strs) {
            if (!map.containsKey(s))
                map.put(s, true);
            else {
                map.put(s, false);
            }
        }
        for (int i = 0; i < strs.length; i++) {
            if (map.get(strs[i]))
                return i;
        }
        return -1;
    }
}


【剑指offer】58. 翻转单词顺序

题目描述
在这里插入图片描述
在这里插入图片描述

// 58. 翻转单词顺序


// 力扣
// 输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。
// 为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a st
// udent. ",则输出"student. a am I"。


// 牛客
// 牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句
// 子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看
// ,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙
// 原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat
// 对一一的翻转这些单词顺序可不在行,你能帮助他么?


题解

// 力扣
// 力扣条件比牛客宽松,不需要考虑s=" "这种东西,遇到空格直接消除就行,
// 构建StringBuilder res保存答案。for循环顺序遍历s.split(" ")字符组的元素,
// 记为str,如果得到的str为" "或者"",直接continue跳过。
// 如果str是正常字符,使用insert插入到res的队首,然后再插入空格" ",
// 如此循环,每次顺序遍历的字符都被从队首插入,这样整个字符串都会变成
// 相反顺序。最后返回trim()修剪空格的res即可。
// 执行用时:3 ms, 在所有 Java 提交中击败了68.19%的用户
// 内存消耗:38.6 MB, 在所有 Java 提交中击败了18.18%的用户
class Solution {
    public String reverseWords(String s) {
        StringBuilder res = new StringBuilder();
        for (String str : s.split(" ")) { 
            if (str.equals(" ") || str.equals(""))
                continue;
            else {
                res.insert(0, str);
                res.insert(0, " ");
            }
        }
        return res.toString().trim();
    }
}



// 牛客
// 牛客与力扣一样,但是多了一个特殊情况判定,特别是如果出现了s=" "这种东西,
// 需要直接返回s,不能把空格删掉了。
// 运行时间:15ms,超过75.79%用Java提交的代码
// 占用内存:10064KB,超过98.42%用Java提交的代码
public class Solution {
    public String ReverseSentence(String s) {
		if(s == null || s.length() == 0 || s.trim().length() == 0)
            return s;
        StringBuilder res = new StringBuilder();
        for (String str : s.split(" ")) { 
            if (str.equals(" ") || str.equals(""))
                continue;
            else {
                res.insert(0, str);
                res.insert(0, " ");
            }
        }
        return res.toString().trim();
    }
}

// 整词处理 - 倒插法
// 力扣
// 执行用时:4 ms, 在所有 Java 提交中击败了43.47%的用户
// 内存消耗:38.4 MB, 在所有 Java 提交中击败了35.34%的用户
class Solution {
    public String reverseWords(String s) {
        s = s.trim();
        if (s.length() == 0)
            return "";

        String[] strs = s.split(" ");
        StringBuilder res = new StringBuilder();
        for (int i = 0; i < strs.length; i++) {
            String str = strs[i].trim();
            if (str.equals(" ") || str.length() == 0) continue;
            StringBuilder temp = new StringBuilder(str);
            res.insert(0, temp.toString() + " ");
        }
        res.delete(res.length() - 1, res.length());
        return res.toString();
    }
}




// 整词处理 - 翻转法
// 力扣
// 执行用时:4 ms, 在所有 Java 提交中击败了43.47%的用户
// 内存消耗:38.4 MB, 在所有 Java 提交中击败了35.34的用户
class Solution {
    public String reverseWords(String s) {
        s = s.trim();
        if (s.length() == 0)
            return "";
            
        String[] strs = s.split(" ");
        StringBuilder res = new StringBuilder();
        for (int i = 0; i < strs.length; i++) {
            String str = strs[i].trim();
            if (str.equals(" ") || str.length() == 0) continue;
            StringBuilder temp = new StringBuilder(str);
            temp.reverse();
            res.append(temp.toString() + " ");
        }
        res.delete(res.length() - 1, res.length());
        return res.reverse().toString();
    }
}


【剑指offer】58.2 左旋转字符串

题目描述

在这里插入图片描述
在这里插入图片描述

// 58.2 左旋转字符串


// 力扣
// 字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。
// 请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcde
// fg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。


// 牛客
// 汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,
// 就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你
// 把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出
// 循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!



题解

StringBuilder辅助法

/// StringBuilder辅助法 
// 使用StringBuilder会让这题变得异常简单


// 力扣
// 就是字面意思,以n为分界,n以前的元素移动到后面
// 执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
// 内存消耗:38.4 MB, 在所有 Java 提交中击败了19.52%的用户
class Solution {
    public String reverseLeftWords(String s, int n) {
        StringBuilder res = new StringBuilder();
        String a = s.substring(0, n);
        String b = s.substring(n, s.length());
        res.append(b).append(a);
        return res.toString();
    }
}


class Solution {
    public String reverseLeftWords(String s, int n) {
        StringBuilder res = new StringBuilder();
        res.append(s.substring(n, s.length()));
        res.append(s.substring(0, n));
        return res.toString();
    }
}



// 牛客
// 跟力扣一样,但是多了一个特殊情况排查
// 运行时间:12ms,超过89.64%用Java提交的代码
// 占用内存:9744KB,超过8.61%用Java提交的代码
public class Solution {
    public String LeftRotateString(String s,int n) {
        if (n <= 0 || n >= s.length())
            return s;
        StringBuilder res = new StringBuilder();
        String a = s.substring(0, n);
        String b = s.substring(n, s.length());
        res.append(b).append(a);
        return res.toString();
    }
}

字符操作法

/// 字符操作法 /


// 力扣
// 以n为界,翻转前半部字符顺序,再翻转后半部字符顺序,
// 最后整个字符组翻转顺序,返回即可。
// 执行用时:2 ms, 在所有 Java 提交中击败了41.86%的用户
// 内存消耗:38.4 MB, 在所有 Java 提交中击败了30.67%的用户
class Solution {
    public String reverseLeftWords(String s, int n) {
		if (n <= 0 || n >= s.length())
            return s;
		char[] chars = s.toCharArray();
		reverse(chars, 0, n - 1);
		reverse(chars, n, s.length() - 1);
		reverse(chars, 0, s.length() - 1);
		return new String(chars);
    }
	
	// 翻转函数reverse
	// 左右双指针left right分别指向字符组char[] chars的头尾,
	// 左右指针索引的元素调用交换位置函数swap,之后左指针右移left++
	// 右指针左移right--,重复交换操作,可以把chars翻转。
	private void reverse(char[] chars, int left, int right) {
		while (left < right) 
			swap(chars, left++, right--);
	}
	
	// 交换位置函数
	private void swap(char[] chars, int i, int j) {
		char temp = chars[i];
		chars[i] = chars[j];
		chars[j] = temp;
	}
}



// 牛客
// 运行时间:12ms超过89.64%用Java提交的代码
// 占用内存:9820KB超过5.40%用Java提交的代码
public class Solution {
    public String LeftRotateString(String s,int n) {
        if (n >= s.length() || n <= 0)
            return s;
        char[] chars = s.toCharArray();
        reverse(chars, 0, n - 1);
        reverse(chars, n, s.length() - 1);
        reverse(chars, 0, s.length() - 1);
        return new String(chars);
    }
    
    private void reverse(char[] chars, int left, int right) {
        while (left < right)
            swap(chars, left++, right--);
    }
    
    private void swap(char[] chars, int i, int j) {
        char temp = chars[i];
        chars[i] = chars[j];
        chars[j] = temp;
    }
}


【剑指offer】67. 把字符串转换成整数

题目描述

在这里插入图片描述

// 牛客
// 将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 
// 数值为0或者字符串不是一个合法的数值则返回0

题解

// 牛客
// 牛客比力扣要简单(也更合理),出现其他字符统统不算int,直接返回0。
// 先排除特殊情况,初始化答案res=0。定义boolean值isNegative,
// 判断第一个字符是不是'-',最后返回答案时判断要不要负号。
// for循环遍历str字符,如果第一个字符是符号,跳过。
// 如果c小于0或大于9,直接返回0.
// 否则将当前c的值加到res,res之前的值乘10进位。遍历完,
// 字符回归它应该在的位数上。最后返回即可
// 运行时间:9ms,超过93.78%用Java提交的代码
// 占用内存:9656KB,超过70.77%用Java提交的代码
class Solution {
    public int strToInt(String str) {
        if (str == null || str.length() == 0)
			return 0;
		int res = 0;
		boolean isNegative = str.charAt(0) == '-';
		for (int i = 0; i < str.length(); i++) {
			char c = str.charAt(i);
			if (i == 0 && (c == '+' || c == '-'))
				continue;
			if (c < '0' || c > '9')
				return 0;
			res = res * 10 + (c - '0');
		}
		return isNegative ? -res : res;
    }
}



特殊解——动态规划

【剑指offer】19. 正则表达式匹配

题目

在这里插入图片描述
在这里插入图片描述

// 牛客
// 请实现一个函数用来匹配包含'. '和'*'的正则表达式。模式中
// 的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现
// 任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整
// 个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但与
// "aa.a"和"ab*a"均不匹配。

// 力扣
// 请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符
// '.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包
// 含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例
// 如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"
// 均不匹配	

题解

/// 递归法 //

// 牛客
// 运行时间:13ms
// 占用内存:9728k
public class Solution {
	public boolean match(char[] str, char[] pattern) {
		if (str == null || pattern == null)
			return false;
		int s = 0;  // 初始化str的遍历指针s
		int p = 0;  // 初始化pattern的遍历指针p
		// 匹配函数,匹配成功返回true
		return matchFunc(str, s, pattern, p);
	}

	public boolean matchFunc(char[] str, int s, char[] pattern, int p) {
		// 如果一起到达str和pattern的尾部,匹配成功,返回true
		if (s == str.length && p == pattern.length) 
			return true;
		// 如果pattern遍历完了(匹配符用完了)str没遍历完,返回false
		if (s != str.length && p == pattern.length) 
			return false;
			
		// 如果遍历指针p的下一个是'*'(根据'*'的定义,默认'*'不可能在第一个出现)
		// 且p不超过遍历范围。那么有'*'的情况是最复杂的,这个题目也是主要解决这个问题。
		if (p + 1 < pattern.length && pattern[p + 1] == '*') {
			// 如果p和s遍历字符相匹配(不管是字符相同的匹配还是有万能符'.'的匹配)
			// 此时有三种情况
			if ((s < str.length && str[s] == pattern[p]) || (s < str.length && pattern[p] == '.')) {
				// 第一种情况是:p当前遍历匹配了0个字符(即使p和s当前遍历字符相匹配,pattern[p]
				// 和后面的'*'也有可能代表匹配了0个字符,而把str[s]交给后面字符来匹配)
				// 第二种情况是:pattern[p]和'*'匹配了str的一个字符,此时s右移一次,
				// p右移两次,跨过'*'。
				// 第三种情况是:pattern[p]和'*'匹配了str的两个字符(或以上,但这个“以上”要留给下一个递归来判断)
				// 所以此时s右移一次,p不动
				// 取三种递归的逻辑与,只要其中一种情况是true就行。
				return matchFunc(str, s, pattern, p + 2) 
					|| matchFunc(str, s + 1, pattern, p + 2)
					|| matchFunc(str, s + 1, pattern, p);
			}
			// 如果p和s遍历字符不匹配,由于'*'可以代表前面字符出现0次
			// 所以不能直接返回false,还要继续往下判断。因此p右移两格继续判断
			// 所以这里跟if中返回的matchFunc(str, s, pattern, p + 2)是一样的
			else {
				return matchFunc(str, s, pattern, p + 2);
			}
		}
		// 如果遍历指针p的下一个字符不是'*'
		else {
			// 若满足:(s没超过遍历范围,且s遍历值等于p遍历值),或者
			// 满足:(s没超过遍历范围,且p遍历值此时是万能匹配符'.')
			// 所以这里跟下一个是'*'情况中的第一个if是一样的。
			if ((s < str.length && str[s] == pattern[p]) || (s < str.length && pattern[p] == '.')) {
				// 递归调用matchFunc,s p指针一起右移一位。
				return matchFunc(str, s + 1, pattern, p + 1);
			}
		}
		return false;  // 其他情况返回false
	}
}


// 力扣
// 执行用时:879 ms, 在所有 Java 提交中击败了5.04%的用户
// 内存消耗:36.8 MB, 在所有 Java 提交中击败了96.62%的用户
class Solution {
    public boolean isMatch(String string_s, String string_p) {
        if (string_s == null || string_p == null)
            return false;
        char[] str = string_s.toCharArray();
        char[] pattern = string_p.toCharArray();
        int s = 0;
        int p = 0;
        return matchFunc(str, s, pattern, p);
    }
    
    public boolean matchFunc(char[] str, int s, char[] pattern, int p) {
        if (s == str.length && p == pattern.length)
            return true;
        if (s < str.length && p == pattern.length)
            return false;
        // 如果pattern下一个是'*'
        if (p + 1 < pattern.length && pattern[p + 1] == '*') {
            // 且当前p和s遍历值匹配(不管是字符匹配还是万能符'.'匹配)
            if ((s < str.length && str[s] == pattern[p]) || (s < str.length && pattern[p] == '.')) {
                return matchFunc(str, s, pattern, p + 2)
                    || matchFunc(str, s + 1, pattern, p + 2)
                    || matchFunc(str, s + 1, pattern, p);
            }
            else {
                return matchFunc(str, s, pattern, p + 2);
            }
        }
        else {
            if ((s < str.length && str[s] == pattern[p]) || s < str.length && pattern[p] == '.')
                return matchFunc(str, s + 1, pattern, p + 1);
        }
        return false;
    }
}


/// 动态规划 //
// 在斐波那契数列问题中,动态规划主要在一维数组中进行
// 数组元素中后一个元素状态,主要取决于前一个元素或前一些元素的状态
// 本题的矩阵形式动态规划,产生的主要原因是由于需要比对的对象不只有一个
// 而是有了两个(甚至两个以上),即需要确定的元素的状态,不仅取决于当前
// 数组A的元素状态,还取决于另外一个数组B的元素状态。
// 因此将一个数组A中的元素作为横轴,另一个数组B中元素作为竖轴,就需要在矩阵形式dp。

// 假设str长度为slen,pattern长度为plen,i表示str的遍历索引,
// j表示pattern的遍历索引,题目的子问题其实就是看str[:i]和pattern[:j]是否匹配,
// 而dp[i+1][j+1]就是这个意思,假设str表示纵轴,pattern表示横轴
// dp[i+1][j+1]==true则为匹配,也就 表示遍历str索引为i的元素,
// 与遍历pattern索引为j的元素是匹配的。
// 而dp[0][0]初始化为true,是表示str和pattern为空时也能匹配。


// 牛客
// 运行时间:11ms
// 占用内存:9680k
public class Solution {
	public boolean match(char[] str, char[] pattern) {
		int slen = str.length;
		int plen = pattern.length;
		// 定义dp动态规划矩阵,dp[i][j]表示的是p的前j个字符和s的前i个字符匹配的结果
		boolean[][] dp = new boolean[slen + 1][plen + 1];
		
		dp[0][0] = true;  // dp[0][0]初始化为true
		// 初始化第一行,首行str为空字符串
		// 因此当pattern的偶数位为'*'时才能够匹配,这时'*'看做出现0次
		for (int i = 1; i <= plen; i++) {
			// 默认pattern第一个元素不会是'*',所以不用考虑超出边界
			if (pattern[i - 1] == '*')
				dp[0][i] = dp[0][i - 2]; 
		}
		// 从第1行第1列开始,从左到右,从上到下遍历矩阵每个位置
		// 需要注意,遍历位置索引分别为i-1和j-1(而不是i j)
		for (int i = 1; i <= slen; i++) {  // 遍历str,索引记为i
			for (int j = 1; j <= plen; j++) {  // 遍历pattern,索引记为j
				// 若遍历位置i-1和j-1的元素相匹配(不管是字符匹配还是万能符'.'匹配)
				if (str[i - 1] == pattern[j - 1] || pattern[j - 1] == '.') {
					dp[i][j] = dp[i - 1][j - 1];  // i和j都直接往下遍历一位
				}
				// 若j-1遍历元素为'*'
				else if (pattern[j - 1] == '*') {
					// 则如果j-2(j-1的上一个)元素与当前i-1元素匹配(不管
					// 是字符匹配还是万能符'.'匹配)
					if (pattern[j - 2] == str[i - 1] || pattern[j - 2] == '.') {
						// A |= B为 A = A || B, 则下一个状态dp[i][j]为dp[i][j - 1],
						// dp[i - 1][j]和dp[i][j - 2]这三个状态的逻辑与
						dp[i][j] |= dp[i][j - 1];  // 表示匹配1个
						dp[i][j] |= dp[i - 1][j];  // 表示匹配2个
						dp[i][j] |= dp[i][j - 2];  // 表示匹配0个
					}
					else {  // 如果j-2元素不匹配,则下一状态等于匹配0个的情况
						dp[i][j] = dp[i][j - 2];
					}
				}
			}
		}
		return dp[slen][plen];  // 最后返回最右下角状态
	}

}


// 力扣
class Solution {
    public boolean isMatch(String string_s, String string_p) {
        if (string_s == null || string_p == null)
            return false;
        char[] str = string_s.toCharArray();
        char[] pattern = string_p.toCharArray();
		int slen = str.length;
		int plen = pattern.length;
		// 定义dp动态规划矩阵,dp[i][j]表示的是p的前j个字符和s的前i个字符匹配的结果
		boolean[][] dp = new boolean[slen + 1][plen + 1];
		
		dp[0][0] = true;  // dp[0][0]初始化为true
		for (int i = 1; i <= plen; i++) { 
			if (pattern[i - 1] == '*') 
				dp[0][i] = dp[0][i - 2]; 
		}
		for (int i = 1; i <= slen; i++) {
			for (int j = 1; j <= plen; j++) {
				// System.out.println(dpToString(dp));  
				if (str[i - 1] == pattern[j - 1] || pattern[j - 1] == '.') {
					dp[i][j] = dp[i - 1][j - 1];
				}
				else if (pattern[j - 1] == '*') {
					if (pattern[j - 2] == str[i - 1] || pattern[j - 2] == '.') {
						dp[i][j] |= dp[i][j - 1];
						dp[i][j] |= dp[i - 1][j];
						dp[i][j] |= dp[i][j - 2];
					}
					else {
						dp[i][j] = dp[i][j - 2];
					}
				}
			}
		}
		return dp[slen][plen];
	}
	
	// 打印dpToString的toString函数(不是必须)
	public String dpToString(boolean[][] dp) {
		StringBuilder res = new StringBuilder();
		for (int i = 0; i < dp.length; i++) {
			res.append("[");
			for (int j = 0; j < dp[0].length; j++) {
				if (dp[i][j])
					res.append(" true ");
				else 
					res.append(" false ");
			}
			res.append("]");
			res.append("\r\n");
		}
		return res.toString();
	}
}





特殊解——回溯搜索

【剑指offer】38. 字符串的排列

题目描述

在这里插入图片描述
在这里插入图片描述

// 力扣
// 输入一个字符串,打印出该字符串中字符的所有排列。
// 你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。


// 牛客
// 输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输
// 入字符串abc,则按字典序打印出由字符a,b,c所能排列出来的所有字符
// 串abc,acb,bac,bca,cab和cba。

题解

// 乍一看是递归回溯,递归的时候不需要更新遍历起点start,
// 每次遍历都从头开始遍历。但是每次从头遍历一定会遇到重复字符,我们
// 定义一个boolean[] 记录被使用过的字符,避免重复。我们有:
import java.util.ArrayList;
public class Solution {
    ArrayList<String> res = new ArrayList<>();

    public ArrayList<String> Permutation(String str) {
        if (str.length() == 0)
            return res;
        StringBuilder temp = new StringBuilder();
        boolean[] used = new boolean[str.length()];
        backtracking(str, used, temp);
        return res;
    }
    
    private void backtracking(String str, boolean[] used, StringBuilder temp) {
        if (temp.length() == str.length()) {
            res.add(temp.toString());
            return;
        }
        for (int i = 0; i < str.length(); i++) {
            if (used[i])
                continue;
            temp.append(str.charAt(i));
            used[i] = true;
            backtracking(str, used, temp);
            temp.delete(temp.length() - 1, temp.length());
            used[i] = false;
        }
    }
}


// 跑出来发现通不过,像str="aa"这种字符就无法通过,我们的算法会输出["aa", "aa"],
// 这样的结果。但是系统想要的是["aa"],我们必须考虑str本身包含重复结点的情况。
// 因此需要将str中的字符存入char[]中,然后排序,排序好之后,
// 相同字符会分布到一起。这样就好处理了。
/// 递归回溯法 

// 牛客
// 运行时间:73ms
// 占用内存:12272k
import java.util.ArrayList;
public class Solution {
	// 定义ArrayList作答案存储字符组
	ArrayList<String> res = new ArrayList<>();  

    public ArrayList<String> Permutation(String str) {
		if (str.length() == 0)  // 如果输入是空,直接返回res
			return res;
		char[] char_array = str.toCharArray();  // str转为char[]
		// 定义boolean[]用于记录某字符是否被使用过
		boolean[] used = new boolean[str.length()];
		// 定义StringBuilder temp来存储当前字符的组成
		StringBuilder temp = new StringBuilder();
		// 调用递归函数
		backtrack(char_array, used, temp);
		return res;
    }
	
	// 字符组形式的char_array用于字母元素遍历
	// bool值组的used用于记录对应索引上char_array元素是否用过
	// StringBuilder的temp用于保存当前组合出来的字符
	private void backtrack(char[] char_array, boolean[] used, StringBuilder temp) {
		// 如果匹配出的字符temp长度与char_array长度相同,说明字符匹配完成
		// 返回(递归回溯)
		if (temp.length() == char_array.length) {
			res.add(temp.toString());
			return;
		}
		// for循环遍历char_array中的字符(可用于匹配的元素)、
		// 遍历索引为i
		for (int i = 0; i < char_array.length; i++) {
			// 如果当前元素用过,当前for循环跳过,进入下一次for循环
			if (used[i])
				continue;
			// 如果遍历字符非首字符,且当前遍历字符与前一字符相等
			// 且前一字符未被用过(或用过后已回溯,被重新标记未使用)
			// ,当前for循环跳过,进入下一次for循环
			if (i != 0 && char_array[i] == char_array[i - 1] && !used[i - 1]) 
				continue;
			used[i] = true;  // 标记i索引位字符已使用
			temp.append(char_array[i]);  // 如果字符符合条件,append进去
			// 递归调用,继续排列temp字符组合,直到排出的字符串长度达到上限
			backtrack(char_array, used, temp);
			// 递归出来,删除刚刚已经使用过的字符(下一个for使用下一个字符)
			temp.deleteCharAt(temp.length() - 1);  
			used[i] = false;  // 回溯后,i索引位字符标记为未使用
		}
	}
}


// 力扣
// 执行用时:11 ms, 在所有 Java 提交中击败了64.52%的用户
// 内存消耗:42.8 MB, 在所有 Java 提交中击败了77.37%的用户
import java.util.ArrayList;
class Solution {
	ArrayList<String> res = new ArrayList<>();

    public String[] permutation(String s) {
        if (s.length() == 0)
			return new String[0];
		char[] char_array = s.toCharArray();
		boolean[] used = new boolean[s.length()];
		StringBuilder temp = new StringBuilder();
		Arrays.sort(char_array);  // 力扣一定要加升序,不然过不了!!!
		backtrack(char_array, used, temp);
        String[] result = (String[]) res.toArray(new String[res.size]);
		return result;
    }
	
	private void backtrack(char[] char_array, boolean[] used, StringBuilder temp) {
		if (temp.length() == char_array.length) {
			res.add(temp.toString());
			return;
		}
		for (int i = 0; i < char_array.length; i++) {
			if (used[i])
				continue;
			if (i != 0 && char_array[i] == char_array[i - 1] && !used[i - 1])
				continue;
			used[i] = true;
			temp.append(char_array[i]);
			backtrack(char_array, used, temp);
			temp.deleteCharAt(temp.length() - 1);
			used[i] = false;
		}
	}
}



特殊解——双指针

【剑指offer】48. 最长不含重复字符的子字符串

题目描述

在这里插入图片描述

// 48. 最长不含重复字符的子字符串


// 力扣
// 请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长
// 子字符串的长度。

题解

 双指针滑窗法 + HashSet /

// 力扣
// 左右指针构成滑动窗口,并用一个HashSet维护已被遍历过的字符
// 执行用时:10 ms, 在所有 Java 提交中击败了20.89%的用户
// 内存消耗:38.6 MB, 在所有 Java 提交中击败了34.39%的用户
import java.util.HashSet;
class Solution {
    public int lengthOfLongestSubstring(String s) {
		int res = 0;  // 答案存储
        int right = 0, left = 0;  // 初始化右指针right,左指针left
		// HashSet标记以遍历过的字符,如果字符出现过,会被存进set
		HashSet<Character> set = new HashSet<>();  
		while (right < s.length()) {
			char c = s.charAt(right);  // 提取当前右指针所在字符c
			while (set.contains(c))  // 如果c已经出现过
				// 左指针准备右移,先去掉左指针字符在set中的备份,
				// 说明该字符不再标记为已被使用,之后左指针右移
				set.remove(s.charAt(left++));  
			// 右指针将当前遍历字符存入set,表示该字符已被使用
			set.add(s.charAt(right));  
			right++;  // 然后右指针right右移
			res = Math.max(res, right - left);  // 窗口长度和res中较大者更新为res
		}
		return res;
    }
}


// 执行用时:9 ms, 在所有 Java 提交中击败了30.63%的用户
// 内存消耗:38.2 MB, 在所有 Java 提交中击败了97.32%的用户
import java.util.HashSet;
class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s == null || s.length() == 0)
            return 0;
        HashSet<Character> set = new HashSet<>();
        int left = 0, right = 0;
        int res = 0;
        while (right < s.length()) {
            if (set.contains(s.charAt(right))) {
                set.remove(s.charAt(left++));
            }
            else {
                set.add(s.charAt(right));
                res = Math.max(res, right - left + 1);
                right++;
            }
        }
        return res;
    }
}


// 简化一下
class Solution {
    public int lengthOfLongestSubstring(String s) {
        char[] strs = s.toCharArray();
        if (strs.length == 0)
            return 0;
        HashSet<Character> set = new HashSet<>();
        int res = 0;
        int left = 0, right = 0;
        while (right < strs.length) {
            while (left < right && set.contains(strs[right])) {
                set.remove(strs[left++]);
            }
            if (!set.contains(strs[right])) {
                set.add(strs[right++]);
                res = Math.max(res, right - left);
            }
        }
        return res;
    }
}




// 力扣
// 布尔数组记录元素已被使用
// 执行用时:2 ms, 在所有 Java 提交中击败了100.00%的用户
// 内存消耗:38.5 MB, 在所有 Java 提交中击败了52.88%的用户
class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s.length() == 0) 
            return 0;
        char[] chars = s.toCharArray();  // s转为字符数组
        boolean[] used = new boolean[128];  // 布尔数组used记录元素已被使用
        int left = -1, right = 0, length = chars.length;
        int res = 1;  // 答案保存
		// 右指针往右遍历
        while (right < length) {
            if (!used[chars[right]]) {  // 如果right遍历元素未被使用
                used[chars[right]] = true;  // used记录right当前位置已被使用
                right++;  // right右移
            }
            else {  // 如果right遍历元素已被使用
				// 当left右移至right遍历位置,将使用标记转false后,循环停止
                while(used[chars[right]]) {  
                    left++;  // left右移
					// 第一次whilw循环时,因为right和left为相同元素,
					// 所以left左移一次后,窗口长度才为不重复字符长度,
					// 取窗口长度和res最大者更新res
                    res = Math.max(res, right-left);  
                    used[chars[left]] = false;  // left右移后遍历位标记转false
                }
            }
        }
		// 最后返回res和right-left-1最大者
        return Math.max(res, right-left-1);  
    }
}


// 简化一下
// 执行用时:2 ms, 在所有 Java 提交中击败了100.00%的用户
// 内存消耗:38.6 MB, 在所有 Java 提交中击败了48.11%的用户
class Solution {
    public int lengthOfLongestSubstring(String s) {
        char[] strs = s.toCharArray();
        if (strs.length == 0)
            return 0;
        boolean[] used = new boolean[128];
        int res = 0;
        int left = 0, right = 0;
        while (right < strs.length) {
            while (left < right && used[strs[right]]) {
                used[strs[left++]] = false;
            }
            if (!used[strs[right]]) {
                used[strs[right++]] = true;
                res = Math.max(res, right - left);
            }
        }
        return res;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/579210.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++多态 万字详解

在经历两个多月的备赛后&#xff0c;最终5.21结果出来后自己也比较满意&#xff0c;以一个省三收尾&#xff08;算法类的&#xff09;。 期间每天偶尔学学新知识&#xff0c;然后主要做题&#xff0c;博客也落下了不少&#xff0c;现在开始继续补&#xff08;可能会些许生疏&a…

【剑指offer】数据结构——数组

目录 数据结构——数组直接解【剑指offer】03.数组中重复的数字排序法集合法原地置换 【剑指offer】04. 二维数组中的查找【剑指offer】29. 顺时针打印矩阵【剑指offer】39. 数组中出现次数超过一半的数字【剑指offer】40. 最小的k个数【剑指offer】45. 把数组排成最小的数【剑…

C++ 实现命令行画心形代码,有多个爱心代码,简单可调数据和字符,可装X,也可用于浪漫的表白,可实现跳动、保存等功能

绘制一个简易爱心 #include <stdio.h> #include <Windows.h>int main() {for (float y 1.5f; y > -1.5f; y - 0.1f){for (float x -1.5f; x < 1.5f; x 0.05f){float z x * x y * y - 1;float f z * z * z - x * x * y * y * y;putchar(f < 0.0f ?…

一文带你了解MySQL之基于规则的优化

前言&#xff1a; 大家别忘了MySQL本质上是一个软件&#xff0c;MySQL的并不能要求使用这个软件的人人都是数据库的高手&#xff0c;就像我写这篇文章的时候并不能要求各位在学之前就会了里边的知识。也就是说我们无法避免某些小伙伴写一些执行起来十分耗费性能的语句。即使是…

【youcans 的图像处理学习课】22. Haar 级联分类器

专栏地址&#xff1a;『youcans 的图像处理学习课』 文章目录&#xff1a;『youcans 的图像处理学习课 - 总目录』 【youcans 的图像处理学习课】22. Haar 级联分类器 3. Haar 特征及其加速计算3.1 Haar 特征3.2 Haar 特征值的计算3.3 积分图像3.4 基于积分图像加速计算 Haar 特…

MATLAB 如何以海森频率格子格纸的形式绘制频率分布曲线?

思路&#xff1a;将水文频率在海森格纸上对应的位置算出来&#xff0c;通过更改坐标轴标签制作海森频率格纸 先放参考链接&#xff1a; norminv 如何利用matlab在海森几率格纸上绘制图形 clc clear close all%% 随机生成径流系列&#xff0c;并计算对应频率 q randi(1000,…

Thymeleaf语法和流程控制,简单传值,each循环,if判断,switch.case使用

Thymeleaf怎么调用静态资源的看我之前发过的文章 这个首先在controller创建一个book的类&#xff0c;book的一些属性自己定义记得getsetyix Controller RequestMapping("/book") public class BookController {RequestMapping("/query")public String qu…

建立基于Open vSwitch的GRE隧道

建立基于Open vSwitch的GRE隧道 1. 环境的准备 图6-1 连接拓扑图 如图6-1所示为两台虚拟机连接拓扑图&#xff0c;两台虚拟机ens33网卡&#xff0c;通过虚拟交换机连接在一起&#xff0c;地址网段为30.0.0.0/24。在Docker主机ens33&#xff0c;IP地址为30.0.0.10/24&#xff…

从0到1接入钉钉消息通知

前段时间给项目接入监控告警&#xff0c;消息通知渠道选了钉钉。预算的原因内部办公刚从飞书转回钉钉&#xff0c;飞书消息通知之前就有一套了&#xff0c;测试钉钉从应用授权到消息测试花了不少时间。这里记录下从钉钉开放平台权限申请到消息接收全流程&#xff0c;给后面有同…

SAP-MM-采购申请字段解析

采购申请抬头以及行项目字段解析 1、采购申请类型&#xff1a; 对PR进行分类&#xff1b; 控制PR行项目的编号间隔&#xff1b; 控制PR编号范围&#xff0c;以及是否内/外部给号&#xff1b; 控制PR的屏幕选择格式&#xff1b; 控制PR是否允许凭证抬头审批&#xff0c;如果允…

mybatis-plus实现逻辑删除(详细!)

文章目录 什么是逻辑删除&#xff1f;为什么用到逻辑删除&#xff1f;在springboot使用Mybatis-Plus提供的逻辑删除1、在application.yml配置2、 实体类字段上加上TableLogic注解演示 什么是逻辑删除&#xff1f; 逻辑删除的本质是修改操作&#xff0c;并不是真正的删除&#…

2023/5/28周报

目录 摘要 论文阅读 1、标题和现存问题 2、使用GNN进行文本分类 3、INDUCT-GCN 4、实验准备 5、实验结果 深度学习 1、时空图的种类 2、图在环境中的应用 3、STGNN 总结 摘要 本周在论文阅读上&#xff0c;阅读了一篇InducT-GCN:归纳图卷积文本分类网络的论文。基…

AWVS使用手册

目录 AWVS基本操作 AWVS工具介绍 AWVS界面介绍 AWVS主要操作区域简介 AWVS的功能及特点的功能及特点 AWVS的菜单栏&#xff0c;工具栏简介 AWVS基本功能介绍 AWVS的蜘蛛爬虫功能 AWVS目标目标探测工具 AWVS的子域名探测工具 AWVS的的HTTP嗅探工具 AWVS的的HTTP模糊…

排序算法的时间复杂度、空间复杂度对比总结

参考&#xff1a;八大排序算法的稳定性和时间复杂度

【linux】守护进程(精灵进程)

文章目录 一、TCP服务器日志二、守护进程预备知识2.1 守护进程概念2.2 前台任务和后台任务2.3 进程组与组长ID2.4 前台进程后台进程的切换2.5 自成会话 三、实现守护进程3.1 自建会话setsid3.2 守护进程的条件3.3 代码实现 一、TCP服务器日志 上一章我们写了一个TCP网络服务器…

SAP-MM-计算方案字段解析

01、 “步骤”&#xff1a;标识此条件类型在计算方案中的顺序编号&#xff0c;此编号会影响到后续业务中条件类型的排序&#xff0c;不同条件类型之间的编号最好间隔大一些&#xff0c;这样设置便于以后对计算方案进行扩展&#xff1b; 02、 “计数器”&#xff1…

(哈希表 ) 349. 两个数组的交集 ——【Leetcode每日一题】

❓349. 两个数组的交集 难度&#xff1a;简单 给定两个数组 nums1 和 nums2 &#xff0c;返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。 示例 1&#xff1a; 输入&#xff1a;nums1 [1,2,2,1], nums2 [2,2] 输出&#xff1a;[…

Hbase学习笔记

1 HBase介绍 (1) HBase是什么 HBase是一个开源的非关系型分布式、实时数据库(Nosql)&#xff0c;运行于HDFS文件系统之上&#xff0c;因此key容错地存储海量稀疏的数据。 海量稀疏就是说不能保证每一个key它的列都有value。 关系型数据库&#xff1a;mysql、oracle 非关系…

Midjourney AI绘画咒语与生成的作品(实例)

据说Midjourney出来后&#xff0c;有一大批设计师抱着电脑&#xff0c;哭晕了~~ 有兴趣的朋友&#xff0c;可以看一下我前两天发的&#xff0c;地址如下&#xff1a; Midjourney AI绘画中文教程详解&#xff08;完整版&#xff09;模型、命令、参数与各种高级用法https://blog…

马斯洛的的五层需求完美吗 不

马斯洛五层需求难道就很完美吗&#xff1f;不&#xff01; 我很佩服的复旦大学哲学王德峰教授批判 对马斯洛的自我实现&#xff0c;他认为 中国人没有西方意义上的自我实现 比如儒家成人达己、圆满、道问学和尊德性&#xff0c;比如孔子的学生颜回 比如佛家的渡人、觉悟。比如弘…