牛客网BAT算法精讲总结2-字符串(KMP算法详解)

1. kmp算法

kmp算法是字符串匹配经典算法。 字符串匹配算法复杂度为 O(m*n), 而kmp算法可以降到 O(m+n)
算法思路: 这个算法的切入点在于,当我们进行匹配,匹配到某个字符不一样时,前面匹配过的是字符 模式串和主串是一样的,即比如
主串: abaaabab
模式串: abab
假设我们把模式串分为三部分, 最长前缀+中间+最长后缀,(这里最长前缀和最长后缀相等),则会出现两种情况:

  • 如果不存在相等的最长前缀和最长后缀,那么再容易不过了,前面匹配过的字符中不存在模式串的开头,那直接模式串从头开始,与主串下一位匹配。
    abbabbbabbb
    abbb
    bb中不存在开头a,所以直接下一步
    abbabbbabbb
           a

  • 如果存在,那么就从这个最长前缀下一位开始匹配,即:
    abaaabab
    abab
    下一步:
    abaaabab
        abab

于是字符串匹配问题的关键就在于next数组怎么求(next数组就是指最长前缀和最长后缀的长度)
求next的思路则是递归思路, 切入点是递归思路,
我感觉这里才是这个算法的难点,很多讲解这个算法思路的,到这一步含糊不清。

要知道长度n的字符串的next,我们只要按知道了 n-1 位的next ,

  • 如果他们下一位相等则+1

  • 如果不等, 比方说:
    abab b abab a
    到第10位,我们发现前面9位的next也就是4,str[4] != str[10], 即黑色的字母不等
    此时说明之前那个前缀必须放弃,选择更短的。 而这里有的信息就是 abab 和 abab 是一样的,那么它的next (aa的next为2),如果和当前位相同,即str[2] == str[10],
    abab b abab a
    我们可以确保蓝色部分是相等的,所以只要str[2] == str[10], 那么next[10] = next[next[10-1]] + 1;

    /**
     *  next的求法,这里设置next[0] = -1, 可以巧妙地作为一个跳出循环的条件
     **/
    private static int[] getNext(String str) {
        int[] next = new int[str.length()];
        next[0] = -1;

        for(int i =1; i < str.length(); i++) {
            int temp = next[i-1];
            while (temp != -1 && str.charAt(temp) != str.charAt(i-1) ) {
                temp = next[temp];
            }
            next[i] = temp + 1;
        }
        return next;
    }
    //kmp算法
    private static List<Integer> kmpMatch(String str1, String str2) {
        int next[] = getNext(str2);
        List<Integer> pos = new ArrayList<>();
        int j = 0;//主串初始位置
        int s = 0;//匹配串初始位置
        while (j < str1.length()) {
            if(s==-1 || str1.charAt(j) == str2.charAt(s)) {
                j++;
                s++;
                if(s == str2.length()) {
                    pos.add(j-s);
                    s = 0;
                    j--; //多加的给减回去
                }
            }else{
                s = next[s];
            }
        }
        return pos;
    }

2. 旋转词

题意: 1234 的旋转词有2341, 3412, 4123,要求判定某个字符串是否为另外一个字符串的旋转词
思路: 将原字符串复制一次加在后面,即 12341234, 然后采用kmp算法,匹配成功与否就是答案。

    //旋转词  1234 的旋转词有2341, 3412, 4123
    public static boolean Isinverse(String str1, String str2) {
        //先判断长度是否相等
        if(str1.length() != str2.length()) {
            return false;
        }
        //str1两次合并
        String temp = str1+str1;
        //判断str2 是否在 str1内
        List<Integer> list = kmpMatch(temp,str2);
        if(list.size() == 0) {
            return false;
        }else {
            return true;
        }
    }

3. 单词逆序

题意:将一句英语中的单词逆序,比如“ I love you” 变成 “you love I”
思路:先整个句子字母逆序“uoy evol I” 再每个单词字母逆序, “you love I”

/**
     * 单词逆序
     */
    private static String inverseWord(String str) {
        String temp = inverse(str,0,str.length()-1);
        int start = 0;
        for(int i=0; i<temp.length(); i++) {
            if(temp.charAt(i) == ' ') {
                temp = inverse(temp, start, i-1);
                start = i+1;
            }
        }
        return inverse(temp,start,temp.length()-1);
    }

    /**
     * 字母逆序
     **/
    private static String inverse(String str,int start, int end) {
        char[] array = str.toCharArray();
        for(int i=start,j=end; i<j; i++,j--) {
            char temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }
        return String.valueOf(array);
    }

4. 交换字符串左右部分

题意:把字符串左右部分交换,但不能借助其他空间
思路:把整个字符串逆序一次,再把前后两部分分别逆序回去。感觉逆序中也是要借助其他空间啊,呵呵。姑且这么做吧。

    /**
     * 不借助空间,实现左右部分交换,前n个换到右边去
     */
    private static String exchange(String str, int n) {
        str = inverse(str,0,n-1);
        str = inverse(str,n,str.length()-1);
        return inverse(str,0,str.length()-1);
    }

5. 两个字符串拼接字典顺序最大

注意一个特殊情况就是了。
ba b 虽然 b的字典序小于ba,但是拼接起来最小的是 bab 而不是 bba
所以应该比较 o1+o2 和 o2+o1, 不应该只是 o1 和 o2比较

  /**
     * 两个字符串拼接字典顺序最小(注意判断方法是 str1+str2 <or> str2+str1)
     */
    private static String findSmallest(String[] strs) {
        if(strs == null || strs.length == 0) {
            return null;
        }
        Arrays.sort(strs,new MyComparator());
        String result = "";
        for(int i=0; i< strs.length; i++) {
            result+=strs[i];
        }
        return result;
    }
class MyComparator implements Comparator<String> {

    @Override
    public int compare(String o1, String o2) {
        String s1 = o1+o2;
        String s2 = o2+o1;
        return s1.compareTo(s2);
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值