LeetCode 26~30

前言

本文隶属于专栏《LeetCode 刷题汇总》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!

本专栏目录结构请见LeetCode 刷题汇总

正文

幕布

在这里插入图片描述

幕布链接

26. 删除有序数组中的重复项

题解

5 lines C++/Java, nicer loops

i遍历,j占位

class Solution {
    public int removeDuplicates(int[] nums) {
        int i = 0;
        for (int n : nums)
            if (i == 0 || n > nums[i-1])
                nums[i++] = n;
        return i;
    }
}

27. 移除元素

题解

My solution for your reference.

i遍历,j占位

class Solution {
    public int removeElement(int[] nums, int val) {
        int j = 0, n = nums.length;
        for(int i = 0; i < n; i++) if(nums[i] != val) nums[j++] = nums[i];
        return j;
    }
}

28. 实现 strStr()

BF 算法(暴力解法)

题解

32 | 字符串匹配基础(上):如何借助哈希算法实现高效字符串匹配?

代码实现
class Solution {
    public int strStr(String haystack, String needle) {
        //i代表主串指针,j模式串
        int i,j;
        //主串长度和模式串长度
        int halen = haystack.length();
        int nelen = needle.length();
        //循环条件,这里只有 i 增长
        for (i = 0 , j = 0; i < halen && j < nelen; ++i) {
            //相同时,则移动 j 指针
            if (haystack.charAt(i) == needle.charAt(j)) {
                ++j;
            } else {
                //不匹配时,将 j 重新指向模式串的头部,将 i 本次匹配的开始位置的下一字符
                i -= j;
                j = 0;
            }
        }
        //查询成功时返回索引,查询失败时返回 -1;
        return j == nelen ? i - nelen : -1;
    }
}

RK 算法

题解

32 | 字符串匹配基础(上):如何借助哈希算法实现高效字符串匹配?

代码实现
class Solution {
    private int m, n;
    private int[] power;
    public int strStr(String haystack, String needle) {
        int m = haystack.length(), n = needle.length();
        if(n == 0) return 0;
        if(m < n) return -1;
        // power 表示底数为 26,指数为下标的幂
        power = new int[m];
        power[0] = 1;
        for(int i = 1; i < m; i++){
            power[i] = power[i - 1] * 26;
        }
        char[] s = haystack.toCharArray();
        int h = hash(s, 0, n - 1);
        int target = hash(needle.toCharArray(), 0 , n - 1);
        if(target == h) return 0;
        for(int i = 1; i < m - n + 1; i++){
            h = (h - power[n - 1] * (s[i - 1] - 'a')) * 26 + (s[i + n - 1] - 'a');
            if(target == h) return i;
        }
        return -1;
    }

    /**
     * 计算字符数组的 hash 值,即 26 进制对应 10 进制整数
     * @param c 字符数组
     * @param l 左边界
     * @param r 右边界
     */
    private int hash(char[] c, int l, int r){
        int res = 0;
        for(int i = r; i >= l; i--){
            res += (c[i] - 'a') * power[r - i];
        }
        return res;
    }
}

BM算法

题解

33 | 字符串匹配基础(中):如何实现文本编辑器中的查找功能?

代码实现
public class Solution {
    public int strStr(String haystack, String needle) {
        if (needle.isEmpty()) {
            return 0;
        }
        return bm(haystack.toCharArray(), haystack.length(), needle.toCharArray(), needle.length());
    }

    // a,b表示主串和模式串;n,m表示主串和模式串的长度。
    public int bm(char[] a, int n, char[] b, int m) {
        int[] bc = new int[256]; // 记录模式串中每个字符最后出现的位置
        generateBC(b, m, bc); // 构建坏字符哈希表
        int[] suffix = new int[m];
        boolean[] prefix = new boolean[m];
        generateGS(b, m, suffix, prefix);//好后缀
        int i = 0; // j表示主串与模式串匹配的第一个字符
        while (i <= n - m) {
            int j;
            for (j = m - 1; j >= 0; --j) { // 模式串从后往前匹配
                if (a[i + j] != b[j]) break; // 坏字符对应模式串中的下标是j
            }
            if (j < 0) {
                return i; // 匹配成功,返回主串与模式串第一个匹配的字符的位置
            }
            int x = j - bc[a[i + j]];
            int y = 0;
            if (j < m - 1) { // 如果有好后缀的话
                y = moveByGS(j, m, suffix, prefix);
            }
            i = i + Math.max(x, y);
        }
        return -1;
    }

    private void generateBC(char[] b, int m, int[] bc) {
        for (int i = 0; i < 256; ++i) {
            bc[i] = -1; // 初始化bc
        }
        for (int i = 0; i < m; ++i) {
            int ascii = b[i]; // 计算b[i]的ASCII值
            bc[ascii] = i;
        }
    }

    // b表示模式串,m表示长度,suffix,prefix数组事先申请好了
    private void generateGS(char[] b, int m, int[] suffix, boolean[] prefix) {
        for (int i = 0; i < m; ++i) { // 初始化
            suffix[i] = -1;
            prefix[i] = false;
        }
        for (int i = 0; i < m - 1; ++i) { // b[0, i]
            int j = i;
            int k = 0; // 公共后缀子串长度
            while (j >= 0 && b[j] == b[m - 1 - k]) { // 与b[0, m-1]求公共后缀子串
                --j;
                ++k;
                suffix[k] = j + 1; //j+1表示公共后缀子串在b[0, i]中的起始下标
            }
            if (j == -1) prefix[k] = true; //如果公共后缀子串也是模式串的前缀子串
        }
    }

    // j表示坏字符对应的模式串中的字符下标; m表示模式串长度
    private int moveByGS(int j, int m, int[] suffix, boolean[] prefix) {
        int k = m - 1 - j; // 好后缀长度
        if (suffix[k] != -1) return j - suffix[k] + 1;
        for (int r = j + 2; r <= m - 1; ++r) {
            if (prefix[m - r]) {
                return r;
            }
        }
        return m;
    }
}

KMP 算法

题解

34 | 字符串匹配基础(下):如何借助BM算法轻松理解KMP算法?

代码实现
class Solution {
    public int strStr(String haystack, String needle) {
        //两种特殊情况
        if (needle.length() == 0) {
            return 0;
        }
        if (haystack.length() == 0) {
            return -1;
        }
        // char 数组
        char[] hasyarr = haystack.toCharArray();
        char[] nearr = needle.toCharArray();
        //长度
        int halen = hasyarr.length;
        int nelen = nearr.length;
        //返回下标
        return kmp(hasyarr,halen,nearr,nelen);

    }
    public int kmp (char[] hasyarr, int halen, char[] nearr, int nelen) {
        //获取next 数组
        int[] next = next(nearr,nelen);
        int j = 0;
        for (int i = 0; i < halen; ++i) {
            //发现不匹配的字符,然后根据 next 数组移动指针,移动到最大公共前后缀的,
            //前缀的后一位,和咱们移动模式串的含义相同
            while (j > 0 && hasyarr[i] != nearr[j]) {
                j = next[j - 1] + 1;
                //超出长度时,可以直接返回不存在
                if (nelen - j + i > halen) {
                    return -1;
                }
            }
            //如果相同就将指针同时后移一下,比较下个字符
            if (hasyarr[i] == nearr[j]) {
                ++j;
            }
            //遍历完整个模式串,返回模式串的起点下标
            if (j == nelen) {
                return i - nelen + 1;
            }
        }
        return -1;
    }

    public  int[] next (char[] needle,int len) {
        //定义 next 数组
        int[] next = new int[len];
        // 初始化
        next[0] = -1;
        int k = -1;
        for (int i = 1; i < len; ++i) {
            //我们此时知道了 [0,i-1]的最长前后缀,但是k+1的指向的值和i不相同时,我们则需要回溯
            //因为 next[k]就时用来记录子串的最长公共前后缀的尾坐标(即长度)
            //就要找 k+1前一个元素在next数组里的值,即next[k+1]
            while (k != -1 && needle[k + 1] != needle[i]) {
                k = next[k];
            }
            // 相同情况,就是 k的下一位,和 i 相同时,此时我们已经知道 [0,i-1]的最长前后缀
            //然后 k - 1 又和 i 相同,最长前后缀加1,即可
            if (needle[k+1] == needle[i]) {
                ++k;
            }
            next[i] = k;

        }
        return next;
    }
}

29. 两数相除

题解

[C++/Java/Python] Should Not Use “long” Int

abs,a-(b<<x<<1)>=0

class Solution {
    public int divide(int A, int B) {
        if (A == 1 << 31 && B == -1) return (1 << 31) - 1;
        int a = Math.abs(A), b = Math.abs(B), res = 0, x = 0;
        while (a - b >= 0) {
            for (x = 0; a - (b << x << 1) >= 0; x++);
            res += 1 << x;
            a -= b << x;
        }
        return (A > 0) == (B > 0) ? res : -res;
    }
}

30. 串联所有单词的子串

题解

Easy Two-Map Solution (C++/Java)

两层 map

public class Solution {
    public static List<Integer> findSubstring(String s, String[] words) {
        List<Integer> res = new ArrayList<>();
        if(s.length() == 0){
            return res;
        }
        int sn = s.length(), wn = words.length, len = words[0].length();
        Map<String, Integer> map = new HashMap<>();
        for(String word : words){
            map.put(word, map.getOrDefault(word, 0) + 1);
        }
        for(int i = 0; i < len; i++){
            int count = wn, start = i;
            Map<String, Integer> matchMap = new HashMap<>();
            for(int j = i; j < sn; j += len){
                String sub = s.substring(j, Math.min(j + len, sn));
                if(map.containsKey(sub)){
                    matchMap.put(sub, matchMap.getOrDefault(sub, 0) + 1);
                    count--;
                    while(matchMap.get(sub) > map.get(sub)){
                        String remove = s.substring(start, start + len);
                        start += len;
                        matchMap.put(remove, matchMap.get(remove) - 1);
                        count += 1;
                    }
                    if(count == 0){
                        res.add(start);
                    }
                }else{
                    count = wn;
                    start = j + len;
                    matchMap.clear();
                }
            }
        }
        return res;
    }
}
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值