代码随想录二刷Day06字符串:28. 找出字符串中第一个匹配项的下标,459.重复的子字符串,232.用栈实现队列,225.用队列实现栈

28. 找出字符串中第一个匹配项的下标(KMP算法)

文章链接:代码随想录 (programmercarl.com)

思路:KMP算法

(1)需要注意的是在定义next数组函数时,cn有两种含义,其一表示哪个位置的字符与i - 1 位置的字符相比较,其二表示最长公共前后缀

(2)要获取i位置的最长公共前后缀就需要看i - 1位置

Java代码:(看注释!!!)

class Solution {
    public int strStr(String haystack, String needle) {
        char[] hay = haystack.toCharArray();
        char[] nee = needle.toCharArray();
        //获取要匹配字符串的next数组
        int[] next = getNextArr(nee);
        //开始使用KMP算法进行匹配
        int i = 0;
        int j = 0;
        while(i < hay.length && j < nee.length){
            //两个字符匹配的上
            if(hay[i] == nee[j]){
                i++;
                j++;
            }

            //没有匹配的上,此时就要开始通过next数组跳位置了
            
            else if(next[j] == -1){//表示此时j指针已经跳不了,匹配不上,i要动一动了
                i++;
            }
            else{//j指针可以跳
                j = next[j];
            }
        }
        //当j越界时,说明nee已经匹配好了,此时一开始的索引位置就是i位置减去长度
        return j == nee.length ? i - j: -1;
    }

    //定义求得next数组的函数
    private int[] getNextArr(char[] ch){
        //先判断特殊情况
        if(ch.length == 1){
            return new int[] {-1};
        }
        int[] next = new int[ch.length];
        //人为规定
        next[0] = -1;
        next[1] = 0;
        //此时开始计算i的最长公共前后缀长度,i从2开始
        int i = 2;
        //cn表示从哪个位置跟i - 1开始比较,要想获取next[i],就要看i - 1 和 cn
        //此外cn还表示最长公共前后缀的长度
        int cn = 0;
        while(i < ch.length){
            if(ch[i - 1] == ch[cn]){
                next[i] = cn + 1;
                i++;
                //此处cn也要++,因为比较完成后,接下来就要求i + 1了,此时就要看i位置
                cn++;
            }
            //没对的上,那么让cn继续往前跳,继续比较
            else if(cn > 0){
                cn = next[cn];
            }
            //没对的上,并且cn不能跳了
            else{
                next[i] = 0;
                i++;
            }
        }
        return next;
    }
}

459.重复的子字符串

文章链接:代码随想录 (programmercarl.com)

思路:

(1)思路一,使用移动匹配,如果一个非空字符串s可以通过由它的一个子串重复多次构成,那么 s + s 组成的新字符串的中间位置一定能找到s,注意遍历s + s 时,要去除首尾位置,然后开始遍历

(2)思路二,使用KMP,题目转换为在s + s 中 是否能找到 s

巩固的知识:

(1)string.indexOf(str):返回指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1

Java代码:使用移动匹配

class Solution {
    public boolean repeatedSubstringPattern(String s) {
        if(s.length() == 0 || s.equals("")){
            return false;
        }
        String ns = s + s;
        StringBuilder sb = new StringBuilder();
        //去除首尾位置
        for(int i = 1; i < ns.length() - 1;i++){
            sb.append(ns.charAt(i));
        }
        //去除了首尾字符的字符串
        String news = new String(sb);
        //找是否有s
        return news.indexOf(s) != -1 ? true : false;
    }
}

232.用栈实现队列(二刷仍然卡顿

文章链接:代码随想录 (programmercarl.com)

思路:

(1)栈是先入后出,队列是先入先出,因此要想用栈实现队列,需要两个栈(in栈和out栈),in栈负责将元素压入到栈然后弹出顶部元素给out栈out栈负责弹出,此时out栈弹出元素的顺序就跟队列是一致的了

(2)此时in栈和out栈两个整体相当于就是队列

实现代码遇到的问题:

(1)在定义empty()时,应该是in和out均为空的时候才是true,因为n栈和out栈两个整体相当于就是队列

(2)在push函数部分,想复杂了,直接push到in栈就行了

Java代码:

class MyQueue {
    Stack<Integer> in;
    Stack<Integer> out;
    public MyQueue() {
        //在构造器里先初始化栈,in栈负责将元素压入in栈然后弹出顶部元素给out栈,out栈负责将元素弹出(out栈相当于是队列)
        in = new Stack<>();
        out = new Stack<>();
    }
    
    public void push(int x) {
        //out栈相当于就是队列
        //相当于是向队列添加元素
        in.push(x);
    }
    
    public int pop() {
        //out栈相当于就是队列
        //相当于是返回队列的开头元素
        if(out.isEmpty()){
            inToOut();
        }
        return out.pop();
    }
    
    public int peek() {
        if(out.isEmpty()) inToOut();
        return out.peek();
    }
    
    public boolean empty() {
        return in.isEmpty() && out.isEmpty();
    }

    //用来将in栈里的全部元素移动到out栈里面
    private void inToOut(){
        if(in.isEmpty()) return;
        while(!in.isEmpty()){
            out.push(in.pop());
        }
    }
}

/**
 * Your MyQueue object will be instantiated and called as such:
 * MyQueue obj = new MyQueue();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.peek();
 * boolean param_4 = obj.empty();
 */

225.用队列实现栈(二刷仍然卡顿

文章链接:代码随想录 (programmercarl.com)

思路:也是需要两个队列来实现栈,唯一不同的地方在于

(1)队列用的是offer和poll,分别是加入和弹出元素的方法

(2)push()函数里,在que2每次添加完元素后,都要将que1的元素加入到que2,然后再俩俩交换que1和que2

(3)当时就思考为什么在实现pop()函数时不判断是否为空,理由如下:队列接口的poll()方法返回并移除容器前面的元素。它删除容器中的元素。当Queue为空时,该方法不会引发异常,而是返回null

(4)单个队列思路就是在push时,每次都重新对队列里的数进行排序,确保新加入的数永远在队列首

Java代码:(两个队列)(看代码注释!!!

class MyStack {
    //栈是先入后出
    Queue<Integer> queue1;//和栈中保持一致的队列
    Queue<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());
        }
        Queue<Integer> temp = queue1;
        queue1 = queue2;
        queue2 = temp;
    }
    
    public int pop() {
        //不需要判断是否为空,poll()会返回null,不会报错
        return queue1.poll();
    }
    
    public int top() {
        return queue1.peek();
    }
    
    public boolean empty() {    
        return queue1.isEmpty();
    }
}

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack obj = new MyStack();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.top();
 * boolean param_4 = obj.empty();
 */

Java代码:单个队列

class MyStack {
    Queue<Integer> que;
    public MyStack() {
        que = new LinkedList<>();
    }
    
    public void push(int x) {
        que.offer(x);
        int size = que.size();
        while(size > 1){
            que.offer(que.poll());
            size--;
        }
    }
    
    public int pop() {
        return que.poll();
    }
    
    public int top() {
        return que.peek();
    }
    
    public boolean empty() {
        return que.isEmpty();
    }
}
 
/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack obj = new MyStack();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.top();
 * boolean param_4 = obj.empty();
 */

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值