20230919刷题记录

20230919刷题记录
参考代码随想录来刷的。

关键词:字符串、KMP

1 28. 实现 strStr()

力扣题目链接
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。

示例 1: 输入: haystack = “hello”, needle = “ll” 输出: 2

示例 2: 输入: haystack = “aaaaa”, needle = “bba” 输出: -1

暴力法

一个指针 i 指向主串字符,一个指针 j 指向模式串字符,
比较 i、j 指向的字符,
如果不相等,则 i 指向 i 初始值的下一位;
如果相等,则 i、j 都向后移动一位,再次比较,知道 j 等于模式串的长度或者不相等为止。
时间复杂度显然是O(m*n)。

KMP

思想是让指向主串字符的指针不回溯,只让指向模式串的指针回溯。

KMP的主要思想是当出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,可以利用这些信息避免从头再去做匹配了。

在这里插入图片描述
如图所示,第6个元素不匹配,此时主串中第6之前的元素是已知的,因为跟模式串的前6都匹配。故此时让 i 不变,让 j = 3,再次进行比较。
上述的例子中,为什么要让 j = 3 呢?这涉及到next数组。

前缀

前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串。
例:aab的前缀为
a、ab

后缀

后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。
例:aab的后缀为
b、ab

最长相等前后缀长度

aab的最长相等前后缀长度为 2。

前缀表

前缀表是用来回退的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配。

模式串aabaaf
下标012345
前缀表010120

其实感觉前缀表中的数跟下标没有对应关系,例如下标等于4时,前缀表中的数据是2,这个2的含义是当模式串的长度为5时(aabaa),最长相等前后缀的长度是2(aa, aa)。

如何求前缀表

这个给我干懵了。
理解起来很困难。

主要有如下三步:
1 初始化
2 处理前后缀不相同的情况
3 处理前后缀相同的情况

private int[] getNext(String s) {
        int[] next = new int[s.length()];
        // j 指向前缀的末尾,也表示当前最长相等前后缀的长度
        int j = 0;
        // i 指向后缀的末尾,同时也在遍历整个模式串。
        for (int i = 1; i < s.length(); i++) {
            while (j > 0 && s.charAt(j) != s.charAt(i))
                j = next[j - 1];
            if (s.charAt(j) == s.charAt(i))
                j++;
            next[i] = j;
        }

        return next;
    }

我现在理解了为什么s.charAt(j) == s.charAt(i)时,要让j++。
理解不了当s.charAt(j) != s.charAt(i)要令j = next[j - 1];

next数组

好像是有很多版本,都是前缀表的变形,如将前缀表中的所有元素向后移动一位,然后让第一个元素的值等于-1。

代码

class Solution {
    public int strStr(String haystack, String needle) {
        int i = 0;
        int j = 0;
        int[] next = getNext(needle);
        while (i < haystack.length() && j < needle.length()) {
            if (j == -1 || haystack.charAt(i) == needle.charAt(j)) {
                ++i;
                ++j;
            } else {
                if (j > 0) j = next[j - 1];
                else j = -1;
            }
        }

        if (j >= needle.length())
            return i - needle.length();
        else
            return -1;
    }


    private int[] getNext(String s) {
        int[] next = new int[s.length()];
        // j 指向前缀的末尾,也表示当前最长相等前后缀的长度
        int j = 0;
        for (int i = 1; i < s.length(); i++) {
            while (j > 0 && s.charAt(j) != s.charAt(i))
                j = next[j - 1];
            if (s.charAt(j) == s.charAt(i))
                j++;
            next[i] = j;
        }
        
        return next;
    }
}

2 232.用栈实现队列

力扣题目链接
感觉很昏,逻辑不是很清晰。

class MyQueue {

    private Stack<Integer> inStack;
    private Stack<Integer> outStack;
    public MyQueue() {
        inStack = new Stack<>();
        outStack = new Stack<>();
    }

    public void push(int x) {
        while (!outStack.isEmpty()) {
            inStack.push(outStack.pop());
        }
        inStack.push(x);
    }

    public int pop() {
        while (!inStack.isEmpty()) 
            outStack.push(inStack.pop());
        
        return outStack.pop();
    }

    public int peek() {
        if (!outStack.isEmpty())
            return outStack.peek();
        
        while (!inStack.isEmpty())
            outStack.push(inStack.pop());
        return outStack.peek();
    }

    public boolean empty() {
        return inStack.isEmpty() && outStack.isEmpty();
    }
}


3 225. 用队列实现栈

力扣题目链接

class MyStack {

    private LinkedList<Integer> queue1;
    private LinkedList<Integer> queue2;
    public MyStack() {
        queue1 = new LinkedList<>();
        queue2 = new LinkedList<>();
    }

    public void push(int x) {
        queue2.offer(x);
        while (!queue1.isEmpty()) {
            queue2.offer(queue1.poll());
        }

        LinkedList<Integer> temp = queue1;
        queue1 = queue2;
        queue2 = temp;
    }

    public int pop() {
        return queue1.poll();
    }

    public int top() {
        return queue1.peek();
    }

    public boolean empty() {
        return queue1.isEmpty();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值