1.最长回文子串

https://leetcode-cn.com/problems/longest-palindromic-substring/

子串的种类 O(n^2);
子序列O(n^3);

  1. 暴力法:相向型双指针。for 起点 for 重点 {判断是否是回文串} 时间复杂度O(N^3)image.png异常检测要注意,忌讳看到i j k等的命令,最好是完整的单词,合理的缩进~,缩进不要超过三层,合理的封装。image.png
  2. 马拉车算法,O(n),但是不是面试官想要实现的算法,原因:写出来了只会证明你是背的。面试官可能也不会~
  3. 基于中心线枚举的算法。[优化思路,内层的回文串已经判断了一次,外层的回文串还是要重新判断一次。abcba]。。。奇数长度的回文串中心点有n个,n - 1个偶数长度的回文串的中心。image.pngimage.png

不能有重复代码!!!image.png工程中尽量避免全局变量。

  1. public class Solution {
  2. public String longestPalindrome(String s) {
  3. if (s == null || s.length() == 0) {
  4. return "";
  5. }
  6. int start = 0, len = 0, longest = 0;
  7. for (int i = 0; i < s.length(); i++) {
  8. len = findLongestPalindromeFrom(s, i, i);
  9. if (len > longest) {
  10. longest = len;
  11. start = i - len / 2;
  12. }
  13. len = findLongestPalindromeFrom(s, i, i + 1);
  14. if (len > longest) {
  15. longest = len;
  16. start = i - len / 2 + 1;
  17. }
  18. }
  19. return s.substring(start, start + longest);
  20. }
  21. private int findLongestPalindromeFrom(String s, int left, int right) {
  22. int len = 0;
  23. while (left >= 0 && right < s.length()) {
  24. if (s.charAt(left) != s.charAt(right)) {
  25. break;
  26. }
  27. len += left == right ? 1 : 2;
  28. left--;
  29. right++;
  30. }
  31. return len;
  32. }
  33. }
  1. 动态规划:两头相等+ 中间也是一个回文串。(状态 + 状态转移。)image.png
  2. image.png

    1. public class Solution {
    2. public String longestPalindrome(String s) {
    3. if (s == null || s.length() == 0) {
    4. return "";
    5. }
    6. int n = s.length();
    7. boolean[][] isPalindrome = new boolean[n][n];
    8. int longest = 1, start = 0;
    9. for (int i = 0; i < n; i++) {
    10. isPalindrome[i][i] = true;
    11. }
    12. for (int i = 0; i < n - 1; i++) {
    13. isPalindrome[i][i + 1] = s.charAt(i) == s.charAt(i + 1);
    14. if (isPalindrome[i][i + 1]) {
    15. start = i;
    16. longest = 2;
    17. }
    18. }
    19. for (int i = n - 1; i >= 0; i--) {
    20. for (int j = i + 2; j < n; j++) {
    21. isPalindrome[i][j] = isPalindrome[i + 1][j - 1] &&
    22. s.charAt(i) == s.charAt(j);
    23. if (isPalindrome[i][j] && j - i + 1 > longest) {
    24. start = i;
    25. longest = j - i + 1;
    26. }
    27. }
    28. }
    29. return s.substring(start, start + longest);
    30. }
    31. }

    插播:面试考点

  3. 不一定要用最优复杂度的算法来解决问题。

  4. 代码真的不是写出来就可以过。代码质量
    1. bug free
    2. 好的代码风格:变量命名规范,合理的使用空格 善用空行。
    3. 容易让人读懂的逻辑,复杂的事情简单化。
    4. 没有冗余代码
    5. 有边界检测和 异常处理
  5. 优化Coding Quality

image.pngimage.png

2.字符串查找

  1. 面试分析
    1. kmp这个考点不太好。image.png- substring这个方法好像也不能用,而且会花时间额外创建一个字符串。空间 时间浪费。image.png
    2. 真问我比n^2 更好的算法?
  2. 实际算法

Rabin-karp算法:加速判断字符串是否相等的过程。整数的比较~ ==》 字符串变成整数,一一对应关系。也就是哈希函数image.png
abc-bcd怎么变?

加速了hashcode不一样的话,原来的字符串一定不一样的情况。
只有hashcode相同的时候,才会做目标串长度次的比较。

乘以任何一个数都可以,31比较常用。经验值,还可以% 一个数 10^6

  1. public class Solution {
  2. /**
  3. * @param source a source string
  4. * @param target a target string
  5. * @return an integer as index
  6. */
  7. public int strStr2(String source, String target) {
  8. if(target == null) {
  9. return -1;
  10. }
  11. int m = target.length();
  12. if(m == 0 && source != null) {
  13. return 0;
  14. }
  15. if(source == null) {
  16. return -1;
  17. }
  18. int n = source.length();
  19. if(n == 0) {
  20. return -1;
  21. }
  22. // mod could be any big integer
  23. // just make sure mod * 33 wont exceed max value of int.
  24. int mod = Integer.MAX_VALUE / 33;
  25. int hash_target = 0;
  26. // 33 could be something else like 26 or whatever you want
  27. for (int i = 0; i < m; ++i) {
  28. hash_target = (hash_target * 33 + target.charAt(i) - 'a') % mod;
  29. if (hash_target < 0) {
  30. hash_target += mod;
  31. }
  32. }
  33. int m33 = 1;
  34. for (int i = 0; i < m - 1; ++i) {
  35. m33 = m33 * 33 % mod;
  36. }
  37. int value = 0;
  38. for (int i = 0; i < n; ++i) {
  39. if (i >= m) { //达到了目标串长度,就删去最左边的。
  40. value = (value - m33 * (source.charAt(i - m) - 'a')) % mod;
  41. }
  42. value = (value * 33 + source.charAt(i) - 'a') % mod;
  43. if (value < 0) {
  44. value += mod;
  45. }
  46. if (i >= m - 1 && value == hash_target) {
  47. // 重新计算。hashcode值相等的时候再做一次检查。
  48. if (target.equals(source.substring(i - m + 1, i + 1))) {
  49. return i - m + 1;
  50. }
  51. }
  52. }
  53. return -1;
  54. }
  55. }

3. 算法的复杂度理论

  1. 时间复杂度-核心考察点
    1. 多项式复杂度
      1. 时间复杂度O(n)的算法:双指针,打擂台【枚举法】,单调栈,单调队列
    2. 非多项式复杂度
  2. 空间复杂度-次要考察点
  3. 编程复杂度-能看得懂
  4. 思维复杂度-能想得出

    4. 双指针的算法

    注意O(max(m,n)) = O (m+ n),逼近法。

image.png

两边往中间走。
image.png

4.1 有效回文串

判断一个字符串忽略大小写和非法字符之后是一个回文串。

  1. public class Solution {
  2. public boolean isPalindrome(String s) {
  3. if (s == null || s.length() == 0) {
  4. return true;
  5. }
  6. int front = 0;
  7. int end = s.length() - 1;
  8. while (front < end) {
  9. while (front < s.length() && !isvalid(s.charAt(front))) { // nead to check range of a/b
  10. front++;
  11. }
  12. if (front == s.length()) { // for empty string “.,,,”
  13. return true;
  14. }
  15. while (end >= 0 && ! isvalid(s.charAt(end))) { // same here, need to check border of a,b
  16. end--;
  17. }
  18. if (Character.toLowerCase(s.charAt(front)) != Character.toLowerCase(s.charAt(end))) {
  19. break;
  20. } else {
  21. front++;
  22. end--;
  23. }
  24. }
  25. return end <= front;
  26. }
  27. private boolean isvalid (char c) {
  28. return Character.isLetter(c) || Character.isDigit(c);
  29. }
  30. }

4.2 回文串二

是否可以在去掉一个字符的情况下成为回文串。
image.pngimage.png

4.3 两数之和

相向型双指针。
哈希表 | 排序 + 双指针。

Follow Up 1:

  • 如果输入数据已经排序,哪个算法更好?

Follow Up 2:

  • 如果需要返回所找的两个数在数组中的下标,哪个算法更好?【双指针带着index一起排序,二元组定义一个Pair】

    public class Solution {
      class Pair implements Comparable<Pair> {
          int number, index;
    
          public Pair(int number, int index) {
              this.number = number;
              this.index = index;
          }
    
          public int compareTo(Pair other) {
              return number - other.number;
          }
      }
      /**
       * @param numbers: An array of Integer
       * @param target: target = numbers[index1] + numbers[index2]
       * @return: [index1, index2] (index1 < index2)
       */
      public int[] twoSum(int[] numbers, int target) {
          int[] result = {-1, -1};
    
          if (numbers == null) {
              return result;
          }
    
          Pair[] pairs = getSortedPairs(numbers);
    
          int left = 0, right = pairs.length - 1;
          while (left < right) {
              if (pairs[left].number + pairs[right].number < target) {
                  left++;
              } else if (pairs[left].number + pairs[right].number > target) {
                  right--;
              } else {
                  result[0] = Math.min(pairs[left].index, pairs[right].index);
                  result[1] = Math.max(pairs[left].index, pairs[right].index);
                  return result;
              }
          }
    
          return result;
      }
    
      private Pair[] getSortedPairs(int[] numbers) {
          Pair[] pairs = new Pair[numbers.length];
          for (int i = 0; i < numbers.length; i++) {
              pairs[i] = new Pair(numbers[i], i);
          }
          Arrays.sort(pairs);
    
          return pairs;
      }
    }
    

    5. 排序

    5.1 快速排序

    核心思想是分治。左半部分 右半部分排起来啊!。

为了避免 数组的值全都是一样,然后每次只能分一个有序的,在碰到等于的情况下,也要跳出来。

import java.util.Random;

public class Solution {
    /*
     * @param A: an integer array
     * @return: 
     */
    public Random rand;
    public void sortIntegers2(int[] A) {
        rand = new Random();
        // write your code here
        quickSort(A, 0, A.length - 1);
    }

    public void quickSort(int[] A, int start, int end) {
        if (start >= end) {
            return;
        }

        int index = rand.nextInt(end - start + 1)  + start;
        int pivot = A[index]; // 标杆数。pivot的选取可以随机化。
        int left = start;
        int right = end;

        while (left <= right) {
            while (left <= right && A[left] < pivot) {
                left ++;
            }
            while (left <= right && A[right] > pivot) {
                right --;
            }

            if (left <= right) {
                int temp = A[left];
                A[left] = A[right];
                A[right] = temp;

                left ++;
                right --;
            }
        }
        // A[start... right] 
        quickSort(A, start, right);
        // A[left ... end]
        quickSort(A, left, end);
    }
}
    public int partition(int[] nums,int start,int end) {
        //[start,end+1)
        int randomIndex = (int)(Math.random() * (end + 1 - start)) + start;
        swap(nums,start,randomIndex);
        int pivotNum = nums[start];
        int left = start, right = end;
        while (left < right) {
            while(left < right && nums[right] > pivotNum) {
                right--;
            }
            if (left < right) {
                nums[left] = nums[right];
                left++;
            }
            while(left < right && nums[left] < pivotNum) {
                left++;
            }
            if (left < right) {
                nums[right] = nums[left];
                right--;
            }
        }
        nums[left] = pivotNum;
        return left;
    }