KMP && BFPRT && 马拉车(Manacher)

目录

1.KMP:

Ⅰ.某个字符的"指标"

ⅠⅠ.KMP的使用方法:

ⅠⅠⅠ.理由1:

ⅠⅤ.理由2:

Ⅴ.”指标的获取“:

ⅤⅠ.代码部分:

2.BFPRT(五个基佬):

Ⅰ.题目背景:

ⅠⅠ快排思想:

 ⅠⅠⅠ.BFPRT改进:

3.马拉车:

Ⅰ.代码:


1.KMP:

Ⅰ.某个字符的"指标"

这里我们把指标做个解释:该字符的前缀后缀匹配的最大长度

例如字符串 baabc,字符c的”指标:“,前缀1:b,后缀1:b相匹配,前缀2:ba,后缀2:ba;相匹配,所以我们称字符c的"指标"就是 2

ⅠⅠ.KMP的使用方法:

它是用来判断一个字符串是不是另一个字符串的子串的一种时间复杂度O(N)的高效算法,

在一般的暴力解法中,我们都是从i位置开始一个个比对,如果比对完毕,则二者是子串关系,如果失败,则 i跳到 i+1的位置继续比对。

而再kmp中我们采取以下方法:

根据上图,我们假设字符串str(s)从i位置开始一直到x之前都与字符串match(m)相匹配,然后当二者比对到x、y的时候,发现不相等了,那么字符串s位置的指示器不动,y下面的指示器根据y的”指标“(假设)5,就跳到5位置的t,与x比对,如果相等,那么继续匹配,不相等的话,就跳到字符t的”指标“的位置继续往后走,如果最后发现y跳到第一个字符了,那么匹配失败了,x位置的指示器就往后走一步。

直到x或者y越界来检验是不是子串。

ⅠⅠⅠ.理由1:

为什么可以直接跳到t位置与x继续比呢?,因为前面我们规定了指标的意思,显然前一段(0-t)和后一段(j-x)位置他们相等,没有必要继续比下去了,就直接从t开始比较就好了

ⅠⅤ.理由2:

你凭什么说i-j位置任何一个字符都不会出现与match配对的情况呢?

 我们假设有一个k位置再字符串s里面和m相匹配,显然,再k-x这一段与m-k‘这一段二者的字符串完全相等,但是我们之前又说了i-x这一段与m-y这一段也完全相等,那么我们可以推出k-x这一段不就相当于y字符串的一个后缀吗?那我们m-k’这一段和y的新的后缀应该相等啊??那么y的信息不出错了吗?所以我们不可能在前面的某个位置k找到一个与m相匹配的子串!

Ⅴ.”指标的获取“:

首先我们规定第一个字符的指标是-1,第二个是0;

我们不妨设i的前面一个位置的字符i-1的指标是7,此时我们假设i位置的字符和7位置的字符相等,那么i位置的指标就是7+1,8了,欸你肯有疑问为什么i位置的指标为什么不能更大吗?。。。。假使i位置的指标更大,那么i-1位置的指标不是更大了吗??矛盾。

如果不相等,那么就调大7位置的字符的指标位置的字符出继续获取信息~

ⅤⅠ.代码部分:

public static int[] getNext(char[] match) {
        if (match.length == 1) {//特殊情况
            return new int[] {-1};
        }
        int[] next = new int[match.length];
        //初始条件
        next[0] = -1;
        next[1] = 0;
        int cn = 0, i = 2;
        while (i < match.length) {
            if (match[cn] == match[i - 1]) {//字符如果相匹配
                next[i++] = ++cn;
            } else if (cn > 0) {//往前走
                cn = next[cn];
            } else {//说明任何一个都不匹配,则该位置的“指标”是0;
                next[i++] = 0;
            }
        }
        return next;
    }

    public static boolean KMP (String s, String m) {
        if (s == null || m == null || m.length() < 1 || s.length() < m.length()) {
            return false;
        }
        char[] str = s.toCharArray();
        char[] match = s.toCharArray();
        int[] next = getNext(match);//获取“指标数组”;
        int x = 0, y = 0;
        while (x < str.length && y < match.length) {
            if (str[x] == match[y]) {//相匹配,继续往后走
                x++;
                y++;
            } else if (y == 0) {//如果y走到了0位置,则说明没有一个字符和x匹配,x往后走
                x++;
            } else {//y往前移动看有没有匹配
                y = next[y];
            }
        }
        return y == match.length;
    }

    public static void main(String[] args) {
        String s = "aabbccddafjandfjlknadfjlkhwoiqhfnoqnfjl";
        String m = "jlkhwoi";
        boolean res = KMP(s, m);//检查是否是子串
        System.out.println(res);
    }

 

2.BFPRT(五个基佬):

Ⅰ.题目背景:

给定一个数组,在不完全排序的情况下找出他的第k小元素。

ⅠⅠ快排思想:

在数组中随机选定一个数字,然后围绕它做partition,partition完毕后的边界范围内如果k-1(第k小的数字)恰好在里面,我们就返回,如果在左边界,则继续在左边做partition,同理在右边则在右边继续。

我们知道快排不具有稳定性,我们知道快排的时间复杂度的数学期望是O(N*logN),但是他不稳定,可能退化成O(N^2),在这个题目里面我们并没有进入两个递归,根据Master公式我们可以知道他的时间复杂度的数学期望是O(N),显然他也是一个不稳的算法代码如下

public static void swap(int[] arr, int o1, int o2) {
        int t = arr[o1];
        arr[o1] = arr[o2];
        arr[o2] = t;
    }

    public static int[] partition(int[] arr, int l, int r, int pivot) {
        int less = l - 1, more = r + 1;
        int cur = l;
        while (cur < more) {
            if (arr[cur] < pivot) {
                swap(arr, ++less, cur++);
            } else if (arr[cur] > pivot) {
                swap(arr, --more ,cur);
            } else {
                cur++;
            }
        }
        return new int[] {less + 1, more - 1};
    }

    public static int process(int[] arr, int l, int r, int index) {
        if (l == r) {//如果l == r,说明arr[l]就是我们要找的数字
            return arr[l];
        }
        int pivot = arr[l + (int)((r - l + 1) * Math.random())];//随机取一个数
        int[] range = partition(arr, l, r, pivot);//做partition找边界
        if (index >= range[0] && index <= range[1]) {//正好命中
            return arr[index];
        } else if (index < range[0]) {//小于左边界
            return process(arr, l, range[0] - 1, index);
        } else {//大于右边界
            return process(arr, range[1] + 1, r, index);
        }
    }

    public static int kthMin(int[] arr, int k) {
        return process(arr, 0, arr.length - 1, k-1);
    }

    public static void main(String[] args) {
        int[] arr = {1,3,5,7,2,4,6,8};
        int k = kthMin(arr, 4);//调用函数
        System.out.println(k);
    }

 ⅠⅠⅠ.BFPRT改进:

我们上面谈到快排的思想不稳定,愿意就在于我们选择的这个数字可能很垃圾,让我们的算法退化了,而BFPRT就是用来改进上述问题,让他的算法趋于稳定收敛到O(N);

        思路:

我们把原数组进行分组,五个数字一组,到最后不够五个的几个一组就几个一组,然后把分好组的数组进行排序,取出中位数组成一个数组,然后再取出这个中位数组成的数组的中位数,那我们取出来的这个数字就是天选之子了,其他部分与快排一样了,直接上代码:

public static void swap(int[] arr, int o1, int o2) {
        int t = arr[o1];
        arr[o1] = arr[o2];
        arr[o2] = t;
    }

    public static int[] partition(int[] arr, int l, int r, int pivot) {
        int less = l - 1, more = r + 1;
        int cur = l;
        while (cur < more) {
            if (arr[cur] < pivot) {
                swap(arr, ++less, cur++);
            } else if (arr[cur] > pivot) {
                swap(arr, --more ,cur);
            } else {
                cur++;
            }
        }
        return new int[] {less + 1, more - 1};
    }

    public static int medianOfMedians(int[] arr, int l, int r) {
        int size = r - l + 1;
        int offSet = size % 5 == 0 ? 0 : 1;//看要分几组
        int[] mArr = new int[size / 5 + 1];
        for (int team = 0; team < mArr.length; team++) {
            int teamFirst = l + team * 5;
            mArr[team] = getMedian(arr, teamFirst, Math.min(teamFirst + 4, r));//分好组之后获得中位数
        }
        return bfprt(mArr, 0, mArr.length - 1, mArr.length / 2);//获得中位数的中位数
    }

    public static int getMedian(int[] arr, int l, int r) {
        insertionSort(arr, l, r);
        return arr[(l + r) / 2];//中位数
    }

    public static void insertionSort(int[] arr, int l, int r) {//常数项很小的插入排序
        for (int i = l + 1; i <= r; i++) {
            for (int j = i - 1; j >= l && arr[j] > arr[j + 1]; j--) {
                swap(arr, j, j + 1);
            }
        }
    }

    public static int bfprt(int[] arr, int l, int r, int index) {
        if (l == r) {
            return arr[l];
        }
        int pivot = medianOfMedians(arr, l, r);//天选之子
        int[] range = partition(arr, l, r, pivot);
        if (index >= range[0] && index <= range[1]) {
            return arr[index];
        } else if (index < range[0]) {
            return bfprt(arr, l, range[0] - 1, index);
        } else {
            return bfprt(arr, range[1] + 1, r, index);
        }
    }

    public static int kthMin(int[] arr, int k) {
        return bfprt(arr, 0, arr.length - 1, k - 1);
    }

    public static void main(String[] args) {
        int[] arr = {1,4,2,5,3,7,6,8};
        int k = kthMin(arr, 5);
        System.out.println(k);
    }

3.马拉车:

Ⅰ.代码:

 public static char[] manacherString(String str) {
        char[] arr = str.toCharArray();
        char[] ret = new char[2 * str.length() + 1];
        int index = 0;
        for (int i = 0; i < ret.length; i++) {
            ret[i] = (i & 1) == 0 ? '#' : arr[index++];
        }
        return ret;
    }

    public static int Manacher(String str) {
        char[] arr = manacherString(str);//处理字符串
        int[] pArr = new int[arr.length];//最大回文半径存储
        int R = -1 , C = -1;//设置默认值
        int max = -1;
        /**
         * case1 : i再r边界外
         * case2 : i 与 i关于c对称点i'的回文半径全在r范围内
         * case3 : i’的回文半径再r外
         * case4 :i'的回文半径与r重合
         */
        for (int i = 0; i < arr.length; i++) {
            pArr[i] = R > i ? Math.min(R -i, pArr[2 * C - i]) : 1;//与i对应的i‘的最大回文半径
            while (i + pArr[i] < arr.length && i - pArr[i] > -1) {//四个case的情况过滤
                if (arr[i + pArr[i]] == arr[i - pArr[i]]) {
                    pArr[i]++;
                } else {
                    break;
                }
            }
            if (i + pArr[i] > R) {//中心更新
                R = i + pArr[i];
                C = i;
            }
            max = Math.max(max, pArr[i]);
        }
        return max - 1;
    }


    public static void main(String[] args) {
        String str = "aaabbbbcacbbasqjofhjonaduuuuuvuuuuu";//uuuuuvuuuuu
        int maxPlenght = Manacher(str);
        System.out.println(maxPlenght);
    }

太累了写不动了Manacher考的好像也不是特别多了。。。。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值