剑指 Offer 05. 替换空格

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

image.png

  1. class Solution {
  2. public String replaceSpace(String s) {
  3. int length = s.length();
  4. char[] carry = new char[length * 3];
  5. int size = 0;
  6. for(int i = 0; i < length; i++){
  7. char c = s.charAt(i);
  8. if(c == ' '){
  9. carry[size++] = '%';
  10. carry[size++] = '2';
  11. carry[size++] = '0';
  12. }else{
  13. carry[size++] = c;
  14. }
  15. }
  16. String newStr = new String(carry, 0, size);
  17. return newStr;
  18. }
  19. }

剑指 Offer 48. 最长不含重复字符的子字符串

image.png
image.png
image.png
image.png

  1. class Solution {
  2. public int lengthOfLongestSubstring(String s) {
  3. //l、r 分别维护滑动窗口的左右边界
  4. //哈希表用来快速查找元素是否在窗口里面存在
  5. int left = 0 , right = 0, max = 0;
  6. Map<Character,Integer > map = new HashMap<>();
  7. for(int i = 0 ; i < s.length(); i++){
  8. char ch = s.charAt(i);
  9. //如果元素在哈希表中存在,left后移到该重复字符后面,删除该结点
  10. //理论上哈希表里面,left以前的数据不存在了,但是实际上依旧存在
  11. //为防止left倒退,使用max函数,使得left只能后移
  12. if(map.containsKey(ch)){
  13. left = Math.max(left , map.get(ch) + 1);
  14. //map.remove(ch);
  15. }
  16. //更新右边界
  17. right = i;
  18. //此时哈希表中一定不存在该元素了,将新元素添加到表中
  19. map.put(ch , i);
  20. max = Math.max(max , right - left + 1);
  21. }
  22. return max;
  23. }
  24. }

20. 有效的括号

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

image.png

  1. class Solution {
  2. public boolean isValid(String s) {
  3. //判空
  4. if(s.length() == 0) return true;
  5. Stack<Character> stack = new Stack<>();
  6. for(char ch : s.toCharArray()){
  7. if(ch == '(' || ch == '[' || ch == '{'){
  8. stack.push(ch);
  9. }else{
  10. if(stack.isEmpty()){
  11. return false;
  12. }else{
  13. char temp = stack.pop();
  14. if(ch == ')'){
  15. if(temp != '('){
  16. return false;
  17. }
  18. }
  19. if(ch == '}'){
  20. if(temp != '{'){
  21. return false;
  22. }
  23. }
  24. if(ch == ']'){
  25. if(temp != '['){
  26. return false;
  27. }
  28. }
  29. }
  30. }
  31. }
  32. return stack.isEmpty()? true: false;
  33. }
  34. }

剑指 Offer 58 - I. 翻转单词顺序

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

image.png
image.png

  1. class Solution {
  2. public String reverseWords(String s) {
  3. s = s.trim(); //去除收尾空格
  4. int j = s.length() - 1, i = j;
  5. StringBuilder res = new StringBuilder();
  6. while(i >= 0){
  7. while(i >= 0 && s.charAt(i) != ' ') i--; //找到空格
  8. res.append(s.substring(i + 1, j + 1) + " "); //添加单词
  9. while(i >= 0 && s.charAt(i) == ' ') i--; //跳过空格 可能包含多个空格
  10. j = i;
  11. }
  12. return res.toString().trim();
  13. }
  14. }

剑指 Offer 58 - II. 左旋转字符串

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

image.png

  1. class Solution {
  2. public String reverseLeftWords(String s, int n) {
  3. String res = "";
  4. for(int i = n; i < s.length(); i++)
  5. res += s.charAt(i);
  6. for(int i = 0; i < n; i++)
  7. res += s.charAt(i);
  8. return res;
  9. }
  10. }

image.png

  1. class Solution {
  2. public String reverseLeftWords(String s, int n) {
  3. StringBuilder res = new StringBuilder();
  4. for(int i = n; i < s.length(); i++)
  5. res.append(s.charAt(i));
  6. for(int i = 0; i < n; i++)
  7. res.append(s.charAt(i));
  8. return res.toString();
  9. }
  10. }

image.png

剑指 Offer 67. 把字符串转换成整数

写一个函数 StrToInt,实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。

image.png
image.png
image.png

  1. class Solution {
  2. public int strToInt(String str) {
  3. char[] chars = str.toCharArray();
  4. int n = chars.length;
  5. int idx = 0;
  6. while (idx < n && chars[idx] == ' ') { // 去掉前导空格
  7. idx++;
  8. }
  9. if (idx == n) return 0; //去掉前导空格以后到了末尾了
  10. boolean negative = false;
  11. if (chars[idx] == '-') {
  12. //遇到负号
  13. negative = true;
  14. idx++;
  15. } else if (chars[idx] == '+') {
  16. // 遇到正号
  17. idx++;
  18. } else if (!Character.isDigit(chars[idx])) {
  19. // 其他符号
  20. return 0;
  21. }
  22. int ans = 0;
  23. while (idx < n && Character.isDigit(chars[idx])) {
  24. int digit = chars[idx] - '0';
  25. if (ans > (Integer.MAX_VALUE - digit) / 10) {
  26. // 本来应该是 ans * 10 + digit > Integer.MAX_VALUE
  27. // 但是 *10 和 + digit 都有可能越界,所有都移动到右边去就可以了。
  28. return negative? Integer.MIN_VALUE : Integer.MAX_VALUE;
  29. }
  30. ans = ans * 10 + digit;
  31. idx++;
  32. }
  33. return negative? -ans : ans;
  34. }
  35. }

344. 反转字符串

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。

image.png

  1. class Solution {
  2. public void reverseString(char[] s) {
  3. int n = s.length;
  4. for(int left = 0,right = n -1;left < right; ++left, --right){
  5. char tmp = s[left];
  6. s[left] = s[right];
  7. s[right] = tmp;
  8. }
  9. }
  10. }

剑指 Offer 50. 第一个只出现一次的字符

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

  1. class Solution {
  2. public char firstUniqChar(String s) {
  3. HashMap<Character, Boolean> hash = new HashMap<>();
  4. char[] ch = s.toCharArray();
  5. for(char c : ch){
  6. hash.put(c, !hash.containsKey(c)); //先插入true, 重复插入为false
  7. }
  8. for(char c : ch){
  9. if(hash.get(c)){
  10. return c;
  11. }
  12. }
  13. return ' ';
  14. }
  15. }

剑指 Offer 38. 字符串的排列

输入一个字符串,打印出该字符串中字符的所有排列。

image.png
image.png
image.png
image.png

  1. class Solution {
  2. Set<String> res = new HashSet<>();
  3. public String[] permutation(String s) {
  4. if(s == null) return new String[]{};
  5. boolean[] visisted = new boolean[s.length()];
  6. dfs(s, "", visisted);
  7. return res.toArray(new String[res.size()]);
  8. }
  9. private void dfs(String s, String ch, boolean[] visisted){
  10. //边界条件判断,当选择的字符长度等于原字符串长度的时候,说明原字符串的字符都已经
  11. //选完了
  12. if(s.length() == ch.length()){
  13. res.add(ch);
  14. return;
  15. }
  16. for(int i = 0; i < s.length(); i++){ ////每一个节点我们都要从头开始选
  17. char temp = s.charAt(i);
  18. if(visisted[i]) continue; ////已经选择过的就不能再选了
  19. visisted[i] = true; //表示选择当前字符
  20. dfs(s, ch + String.valueOf(temp), visisted); //把当前字符选择后,到树的下一层继续选
  21. visisted[i] = false; //递归往回走的时候要撤销选择
  22. }
  23. }
  24. }

541. 反转字符串 II

给定一个字符串 s 和一个整数 k,从字符串开头算起,每 2k 个字符反转前 k 个字符。

如果剩余字符少于 k 个,则将剩余字符全部反转。 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。

image.png

  1. class Solution {
  2. public String reverseStr(String s, int k) {
  3. char[] a = s.toCharArray();
  4. for(int start = 0; start < a.length; start += 2*k){
  5. int i = start;
  6. int j = Math.min(start + k - 1, a.length - 1);
  7. while(i < j){
  8. char tmp = a[i];
  9. a[i++] = a[j];
  10. a[j--] = tmp;
  11. }
  12. }
  13. return new String(a);
  14. }
  15. }

459. 重复的子字符串

给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。

image.png

  1. class Solution {
  2. public boolean repeatedSubstringPattern(String s) {
  3. return kmp(s);
  4. }
  5. public boolean kmp(String pattern) {
  6. int n = pattern.length();
  7. int[] fail = new int[n];
  8. Arrays.fill(fail, -1);
  9. for (int i = 1; i < n; ++i) {
  10. int j = fail[i - 1];
  11. while (j != -1 && pattern.charAt(j + 1) != pattern.charAt(i)) {
  12. j = fail[j];
  13. }
  14. if (pattern.charAt(j + 1) == pattern.charAt(i)) {
  15. fail[i] = j + 1;
  16. }
  17. }
  18. return fail[n - 1] != -1 && n % (n - fail[n - 1] - 1) == 0;
  19. }
  20. }

image.png
image.png

409. 最长回文串

给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。

在构造过程中,请注意区分大小写。比如 “Aa” 不能当做一个回文字符串。

image.png
image.png
image.png

  1. class Solution {
  2. public int longestPalindrome(String s) {
  3. // 1. counter数组计数,统计每种字母出现的次数
  4. int[] counter = new int[58];
  5. for (char c : s.toCharArray()) {
  6. counter[c - 'A']++;
  7. }
  8. // 2. 构造回文串,每种字母使用偶数次
  9. int res = 0;
  10. for (int x: counter) {
  11. res += x - (x & 1);
  12. }
  13. // 3. 如果最终的长度小于原字符串的长度,说明存在某个字符出现了奇数次,那可以在最中间再补一个字母。
  14. return res < s.length()? res + 1: res;
  15. }
  16. }

647. 回文子串

给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。 具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。 示例 1: 输入:”abc” 输出:3 解释:三个回文子串: “a”, “b”, “c” 示例 2:

输入:”aaa” 输出:6 解释:6个回文子串: “a”, “a”, “a”, “aa”, “aa”, “aaa”

image.png

  1. class Solution {
  2. public int countSubstrings(String s) {
  3. int len, ans = 0;
  4. if (s == null || (len = s.length()) < 1) return 0;
  5. //dp[i][j]:s字符串下标i到下标j的字串是否是一个回文串,即s[i, j]
  6. boolean[][] dp = new boolean[len][len];
  7. for (int j = 0; j < len; j++) {
  8. for (int i = 0; i <= j; i++) {
  9. //当两端字母一样时,才可以两端收缩进一步判断
  10. if (s.charAt(i) == s.charAt(j)) {
  11. //i++,j--,即两端收缩之后i,j指针指向同一个字符或者i超过j了,必然是一个回文串
  12. if (j - i < 3) {
  13. dp[i][j] = true;
  14. } else {
  15. //否则通过收缩之后的字串判断
  16. dp[i][j] = dp[i + 1][j - 1];
  17. }
  18. } else {//两端字符不一样,不是回文串
  19. dp[i][j] = false;
  20. }
  21. }
  22. }
  23. //遍历每一个字串,统计回文串个数
  24. for (int i = 0; i < len; i++) {
  25. for (int j = 0; j < len; j++) {
  26. if (dp[i][j]) ans++;
  27. }
  28. }
  29. return ans;
  30. }
  31. }

5. 最长回文子串

回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串

中心扩散法

image.png
image.png
image.png
image.png
image.png
image.png

动态规划

image.png

  1. class Solution {
  2. public String longestPalindrome(String s) {
  3. if(s.length() == 0) return s;
  4. int res = 1;
  5. int ll = 0, rr = 0;
  6. //中心点为奇数
  7. for(int i = 0; i < s.length(); i++){
  8. int l = i - 1;
  9. int r = i + 1;
  10. while(l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)){
  11. int len = (r - l + 1);
  12. if(len > res){
  13. res = len;
  14. ll = l;
  15. rr = r;
  16. }
  17. l--;
  18. r++;
  19. }
  20. //中心点为偶数
  21. l = i;
  22. r = i + 1;
  23. while(l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)){
  24. int len = (r - l + 1);
  25. if(len > res){
  26. res = len;
  27. ll = l;
  28. rr = r;
  29. }
  30. l--;
  31. r++;
  32. }
  33. }
  34. return s.substring(ll, rr + 1);
  35. }
  36. }

516. 最长回文子序列

给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。 子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。

示例 1:

输入:s = “bbbab” 输出:4 解释:一个可能的最长回文子序列为 “bbbb” 。 示例 2:

输入:s = “cbbd” 输出:2 解释:一个可能的最长回文子序列为 “bb” 。

image.png

  1. public class Solution {
  2. public int longestPalindromeSubseq(String s) {
  3. int len = s.length();
  4. int[][] dp = new int[len + 1][len + 1];
  5. for (int i = len - 1; i >= 0; i--) { // 从后往前遍历 保证情况不漏
  6. dp[i][i] = 1; // 初始化
  7. for (int j = i + 1; j < len; j++) {
  8. if (s.charAt(i) == s.charAt(j)) {
  9. dp[i][j] = dp[i + 1][j - 1] + 2;
  10. } else {
  11. dp[i][j] = Math.max(dp[i + 1][j], Math.max(dp[i][j], dp[i][j - 1]));
  12. }
  13. }
  14. }
  15. return dp[0][len - 1];
  16. }
  17. }