代码随想录算法训练营第九天 _ 字符串_LCR 122. 路径加密、LCR 182. 动态口令、28.找出字符串中第一个匹配项的下标、459.重复的子字符串(KMP算法)。

学习目标:

60天训练营打卡计划!

学习内容:

LCR 122. 路径加密

  • 略简单,和替换空格的难度完全不能比。
class Solution {
    public String pathEncryption(String path) {
        char[] ch = path.toCharArray();

        for(int i = 0; i < ch.length; i++){
            if(ch[i] == '.'){
                ch[i] = ' ';
            }
        }
        return new String(ch);
    }
}

LCR 182. 动态口令

  • 和昨天的左旋数组思路完全一致。
class Solution {
    private String reverse(String st, int left, int right){
        char[] ch = st.toCharArray();

        while(left < right){
            char tmp = ch[left];
            ch[left] = ch[right];
            ch[right] = tmp;
            left++;
            right--;
        }
        return new String(ch);    }

    public String dynamicPassword(String password, int target) {
        int size = password.length();
        return reverse(reverse(reverse(password, 0, target - 1), target ,size - 1), 0, size - 1);
    }
}

KMP算法理论学习

可以用于解决在一个串中查找另一个串(模式串)是否出现过问题,非常适用。

  • 好难,好懵逼!
  • 前缀包含首字符但不含尾字符的所有子字符串
  • 后缀包含尾字符但不含首字符的所有子字符串
  • 前缀表:记录的是模式串(即所求字符串)的子串的最长相等前后缀长度,用于在字符串匹配时做回退(遇到字符不符之后,前缀表可以得到要回退到的位置)的操作。
  • 最长相等前后缀长度计算方法:通过下面的示例说明相等前后缀的对比方法,是一个序列的重复出现,而不是反转后的出现。这样的话,我就可以理解next数组的构造过程!
abcddcba
00000001
abcdabcd
00001234

KMP算法:通过计算 模式串子字符串最长相等前后缀长度 得到一个 前缀表(即next数组或者prefix数组) 来 优化匹配 过程的算法。
在这里插入图片描述

  • 整个KMP算法可以分为:
  • 初始化 – i和j指向的元素不相等时的操作(前后缀不相等的情况) – i和j指向的元素相等时的操作(前后缀相等的情况)-- 更新next值
  • 可以视为一种双指针的过程。
  • 其中i是后缀末尾,i在循环中,一直在向字符串尾移动。
  • 其中j是前缀末尾,另一个含义是:i及i之前的字串中最长的相等前后缀长度。 j的移动过程:当i和j所指的值相等时,j+1;当 i 和 j 所指的值不等时,j一直做减法,直到 j == 0 或者 重新和 i 所指值相等。因为最长相等前后缀的匹配规则 (相等指一个序列平移后相等,并非翻转后相等,故最长相等前后缀长度可以从前几位继承),所以 j 同时能有两个含义。

28.找出字符串中第一个匹配项的下标

  • 整个KMP算法可以分为:
  • 初始化 – i和j指向的元素不相等时的操作(前后缀不相等的情况) – i和j指向的元素相等时的操作(前后缀相等的情况)-- 更新next值
  • 可以视为一种双指针的过程。
  • 其中i是后缀末尾,i在循环中,一直在向字符串尾移动。
  • 其中j是前缀末尾,另一个含义是:i及i之前的字串中最长的相等前后缀长度。 j的移动过程:当i和j所指的值相等时,j+1;当 i 和 j 所指的值不等时,j一直做减法,直到 j == 0 或者 重新和 i 所指值相等。因为最长相等前后缀的匹配规则 (相等 指 一个序列平移后相等,并非翻转后相等,故最长相等前后缀长度可以从前几位继承),所以 j 同时能有两个含义。
class Solution {
    private void getNext(String needle, int[] next){
        // j是前缀尾部,同时也是长度为j-1的子字符串的最大相等前后缀长度
        int j = 0;
        next[0] = 0;
        // i是后缀尾部
        for(int i = 1; i < needle.length(); i++){
            // 当 i 和 j 所指的值不等时,j一直做减法,直到 j == 0 或者 重新和 i 所指值相等。
            while(j > 0 && needle.charAt(i) != needle.charAt(j)){
                j--;
            }
            // 当i和j所指的值相等时,j+1
            if(needle.charAt(i) == needle.charAt(j))  j++;
            next[i] = j;
            System.out.print(next[i] + " ");
        }
    }

    public int strStr(String haystack, String needle) {
        int size = needle.length();
        if(size == 0)   return 0;

        int[] next = new int[size];
        getNext(needle, next);

        // 使用next数组进行匹配
        int j = 0;
        for(int i = 0 ; i < haystack.length(); i++){
            // 遇到字符不符之后,前缀表可以得到要回退到的位置
            while(j >= 1 && haystack.charAt(i) != needle.charAt(j)){
                j = next[j-1];
            }
            if(haystack.charAt(i) == needle.charAt(j)) j++;
            // 此时已经找到了第一个匹配项
            // +2是因为i相对于j的变化有1的滞后性
            if(j == size-1)  return i-size+2;
        }

        // System.out.println("*" + j);
        return -1;
        
    }
}

459.重复的子字符串

该题的KMP算法用于在给定的字符串中查找最小的不重复子字符串的长度。

  • next[size-1]表示该字符串的最长相等前后缀长度,故 size - next[size-1] 就可以表示最小的不重复子字符串。(科学!) 如下表所示的例子。
ababba
001201
  • 对于KMP算法的对于前后缀不相等的情况的处理还是生疏,应该是j = next[j-1],这也是前缀表存在的一个重要意义!自己一定要记好了!
class Solution {
    // 怎么找模式串呢?
    // 放回值为未重复段长度
    private void getNext(String s, int[] next){
        // 前缀尾
        int j = 0;
        next[0] = 0;
        // 后缀尾
        for(int i = 1; i < s.length(); i++){
            // 遇到字符不符之后,前缀表可以得到要回退到的位置
            while(j > 0 && s.charAt(i) != s.charAt(j))  j = next[j-1];
            if(s.charAt(i) == s.charAt(j))  j++;
            // 更新值
            next[i] = j;
            // System.out.print(next[i] + " ");
        }
    }

    public boolean repeatedSubstringPattern(String s) {
        int size = s.length();
        int[] next = new int[size];
        getNext(s, next);
        // next[size-1]该字符串的最长相等前后缀长度,
        // 用长度减掉该值即为真实的不重复子字符串长度
        if(next[size-1] > 0 && size % (size - next[size-1]) == 0)
            return true;
        else
            return false;
    }
}

学习时间:

  • 上午一小时,下午两小时(KMP算法),整理文档半小时
  • 12月13日重新学习KMP算法,并理清了一刷时的遗留问题。目前可以从根源上理解getNext函数的实现。
  • 12月14日第一次自己实现459题,原理还是需要熟悉。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值