第27节.pptx

什么是KMP

假设字符串str长度为N,字符串match长度为M,M <= N想确定str中是否有某个子串是等于match的。时间复杂度O(N)的匹配算法
KMP算法的核心 理解next数组

KMP算法实现

  1. **
  2. * KMP算法实现
  3. *
  4. * @author :Administrator
  5. * @date :2022/5/12 0012
  6. */
  7. public class KMP {
  8. public static int getIndexOf(String s, String m) {
  9. if (s.length() < m.length()) {
  10. return -1;
  11. }
  12. char[] sChars = s.toCharArray();
  13. char[] mChars = m.toCharArray();
  14. int sIndex = 0;
  15. int mIndex = 0;
  16. // next[i] i位置之前,0~i-1,前缀与后缀字符串相匹配的最大长度
  17. int[] next = getNextArray(mChars);
  18. while (sIndex < sChars.length && mIndex < mChars.length) {
  19. // 如果sIndex位置与mIndex位置匹配,则都前进
  20. if (sChars[sIndex] == mChars[mIndex]) {
  21. sIndex++;
  22. mIndex++;
  23. }
  24. // 如果mIndex=0,还无法配置,sIndex前进,m字符串从0开始匹配
  25. else if (next[mIndex] == -1) {
  26. sIndex++;
  27. } else {
  28. // 否则从next[mIndex] 位子开始匹配
  29. mIndex = next[mIndex];
  30. }
  31. }
  32. return mIndex == mChars.length ? sIndex - mIndex : -1;
  33. }
  34. private static int[] getNextArray(char[] mChars) {
  35. if (mChars.length == 1) {
  36. return new int[]{-1};
  37. }
  38. int[] next = new int[mChars.length];
  39. next[0] = -1;
  40. next[1] = 0;
  41. int pos = 2;
  42. int cn = 0;
  43. while (pos < next.length) {
  44. // next[i] -> cn = next[i-1] 如果mChars[i-1] == mChars[cn] 则 next[i] = cn+1;
  45. if (mChars[pos - 1] == mChars[cn]) {
  46. next[pos++] = ++cn;
  47. } else if (cn > 0) {
  48. // 如果mChars[i-1] != mChars[cn] cn = next[cn]
  49. cn = next[cn];
  50. } else {
  51. // 如果cn==0 表示来到mChar[0]!=mChars[pos-1] 则没有前缀和后缀相等的字符串
  52. next[pos++] = 0;
  53. }
  54. }
  55. return next;
  56. }
  57. }

KMP应用

题目1:判断数的子树结构是否与另一个二叉树完全一样

给定两棵二叉树的头节点head1和head2,想知道head1中是否有某个子树的结构和head2完全一样

  1. static class TreeNode {
  2. private int value;
  3. private TreeNode left;
  4. private TreeNode right;
  5. public TreeNode(int value) {
  6. this.value = value;
  7. }
  8. }
  9. public static boolean containsTree1(TreeNode big, TreeNode small) {
  10. if (small == null) {
  11. return true;
  12. }
  13. if (big == null) {
  14. return false;
  15. }
  16. if (isSameValueStructure(big, small)) {
  17. return true;
  18. }
  19. return containsTree1(big.left, small) || containsTree1(big.right, small);
  20. }
  21. public static boolean isSameValueStructure(TreeNode head1, TreeNode head2) {
  22. if (head1 == null && head2 != null) {
  23. return false;
  24. }
  25. if (head1 != null && head2 == null) {
  26. return false;
  27. }
  28. if (head1 == null && head2 == null) {
  29. return true;
  30. }
  31. if (head1.value != head2.value) {
  32. return false;
  33. }
  34. return isSameValueStructure(head1.left, head2.left)
  35. && isSameValueStructure(head1.right, head2.right);
  36. }
  1. static class TreeNode {
  2. private int value;
  3. private TreeNode left;
  4. private TreeNode right;
  5. public TreeNode(int value) {
  6. this.value = value;
  7. }
  8. }
  9. public static boolean containsSameSubTree(TreeNode head1, TreeNode head2) {
  10. if (head2 == null) {
  11. return true;
  12. }
  13. if (head1 == null) {
  14. return false;
  15. }
  16. ArrayList<String> list1 = new ArrayList<>();
  17. ArrayList<String> list2 = new ArrayList<>();
  18. list1 = preProcess(head1, list1);
  19. list2 = preProcess(head2, list2);
  20. return getIndex(list1, list2) != -1;
  21. }
  22. private static ArrayList<String> preProcess(TreeNode head, ArrayList<String> list) {
  23. if (head == null) {
  24. list.add(null);
  25. return list;
  26. }
  27. list.add(String.valueOf(head.value));
  28. preProcess(head.left, list);
  29. preProcess(head.right, list);
  30. return list;
  31. }
  32. private static int getIndex(ArrayList<String> list1, ArrayList<String> list2) {
  33. if (list1 == null || list2 == null || list1.size() < 1 || list1.size() < list2.size()) {
  34. return -1;
  35. }
  36. int n = list1.size(), m = list2.size();
  37. int[] next = getNextArray(list2);
  38. int i1 = 0;
  39. int i2 = 0;
  40. while (i1 < n && i2 < m) {
  41. if (Objects.equals(list1.get(i1), list2.get(i2))) {
  42. i1++;
  43. i2++;
  44. } else if (next[i2] == -1) {
  45. i1++;
  46. } else {
  47. i2 = next[i2];
  48. }
  49. }
  50. return i2 == m ? i1 - i2 : -1;
  51. }
  52. private static int[] getNextArray(ArrayList<String> list) {
  53. if (list == null || list.size() == 0) {
  54. return null;
  55. }
  56. int n = list.size();
  57. if (n == 1) {
  58. return new int[]{-1};
  59. }
  60. int[] next = new int[n];
  61. next[0] = -1;
  62. next[1] = 0;
  63. int pos = 2;
  64. int cn = 0;
  65. while (pos < n) {
  66. if (Objects.equals(list.get(pos - 1), list.get(cn))) {
  67. next[pos++] = ++cn;
  68. } else if (cn > 0) {
  69. cn = next[cn];
  70. } else {
  71. next[pos++] = 0;
  72. }
  73. }
  74. return next;
  75. }

题目2:判断是否为旋转字符串

判断str1和str2是否是旋转字符串。例:str1 : abcdefg ——》旋转字符串:(1) 以0号字符旋转:abcdefg (2) 以1号字符旋转 bcdefga (3)以2号 字符旋转 cdefgab ……

/*
     * 判断str1和str2是否是旋转字符串
     * 例:
     * str1 :  abcdefg ——》旋转字符串:(1) 以0号字符旋转:abcdefg (2) 以1号字符旋转 bcdefga (3)以2号 字符旋转 cdefgab ……
     *         0123456
     * */

public static boolean isRotation(String str1, String str2) {
    if (str1 == null || str2 == null || str1.length() != str2.length()) {
        return false;
    }

    String str = str1.concat(str1);

    return getIndexSubString(str, str2) != -1;
}

private static int getIndexSubString(String s1, String s2) {

    if (s1 == null || s2 == null || s1.length() < 1 || s1.length() < s2.length()) {
        return -1;
    }


    int i1 = 0;
    int i2 = 0;
    char[] chars1 = s1.toCharArray();
    char[] chars2 = s2.toCharArray();

    int[] next = getNextArray(chars2);

    while (i1 < chars1.length && i2 < chars2.length) {
        if (chars1[i1] == chars1[i2]) {
            i1++;
            i2++;
        } else if (next[i2] == -1) {
            i1++;
        } else {
            i2 = next[i2];
        }
    }

    return i2 == chars2.length ? i1 - i2 : -1;
}

private static int[] getNextArray(char[] chars) {
    if (chars.length == 1) {
        return new int[]{-1};
    }
    int[] next = new int[chars.length];

    next[0] = -1;
    next[1] = 0;
    int i = 2;
    int cn = 0;

    while (i < next.length) {
        if (chars[i - 1] == chars[cn]) {
            next[i++] = ++cn;
        } else if (cn > 0) {
            cn = next[cn];
        } else {
            next[i++] = 0;
        }
    }
    return next;
}