重复的子字符串、用栈实现队列、用队列实现栈

0 阅读3分钟

459. 重复的子字符串

 这题有三种解法:暴力求解、移动匹配、KMP解法。暴力求解我不过多说明,我们来看剩下两种解法。

        移动匹配思想:如果一个字符串是由某个子串重复多次组成的,那么把它和自己拼接一次得到s+s后,在中间部分一定还能找到一个完整的原字符串s。但如果字符串本身没有这种重复结构,那么在去掉首尾字符后的s+s中就不可能再出现s。

        所以我们将字符串s拼接在一起,为了防止直接检测出第一个s和最后一个s,我们把第一个字符和最后一个字符删掉,如果这个字符串由多个相同的子串构成,那么其中一定还包含有一个s。代码如下。

class Solution {
    public boolean repeatedSubstringPattern(String s) {
        String ss=s+s;
            ss=ss.substring(1);
            ss=ss.substring(0,ss.length()-1);
            return ss.contains(s);
    }
}

        KMP算法:如果一个字符串是由某个子串重复多次组成的,那么它的最长相等前后缀的长度一定大于0,且这个前后缀长度可以帮助我们算出最小的重复单元长度。
我们先用 KMP 算法计算next数组,其中next[i]表示长度为i+1的子串中,最长的相等前后缀长度。如果next[s.length()-1]>0,那么整个字符串的前缀和后缀有重叠,可能存在重复子串。接着我们用公式 len = s.length() - next[s.length()-1] 得到最小的重复单元长度。如果字符串长度能整除这个重复单元长度,那么这个字符串一定是由该子串重复多次组成的。否则,说明字符串内部虽然有前后缀重叠,但不是由重复子串构成的。代码如下。         

class Solution {
    public boolean repeatedSubstringPattern(String s) {
        int[] next=getNext(s);
        if(next[s.length()-1]>0&&s.length()%(s.length()-next[s.length()-1])==0)
        return true;
        return false;
    }
    public static int[] getNext(String pattern) {
        int n = pattern.length();
        int[] next = new int[n];

        int j = 0;
        next[0] = 0;

        for (int i = 1; i < n; i++) {
            while (j > 0 && pattern.charAt(i) != pattern.charAt(j)) {
                j = next[j - 1];
            }
            if (pattern.charAt(i) == pattern.charAt(j)) {
                j++;
            }
            next[i] = j;
        }
        return next;
    }
}

232. 用栈实现队列

  这道题在我前面java实现队列的文章中写过,理解思想就不难。代码如下:

class MyQueue {
    private Stack<Integer> s1=new Stack<>();
    private Stack<Integer> s2=new Stack<>();

    public MyQueue() {
        
    }
    
    public void push(int x) {
        s1.push(x);
    }
    
    public int pop() {
        if(empty()){
            return -1;
        }
        if(s2.empty()){
            while(!s1.empty()){
                s2.push(s1.pop());
            }
        }
        return s2.pop();
    }
    
    public int peek() {
         if(empty()){
            return -1;
        }
        if(s2.empty()){
            while(!s1.empty()){
                s2.push(s1.pop());
            }
        }
        return s2.peek();
        
    }
    
    public boolean empty() {
        return s1.empty()&&s2.empty();
    }
}

/**
 * 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();
 */

        用 s1 作为输入栈,负责接收新元素;用 s2 作为输出栈,负责出队。当需要 poppeek 时,如果 s2 为空,就把 s1 中的元素全部依次弹出并压入 s2,这样原来先进的元素就会来到 s2 的栈顶,从而实现队列的先进先出(FIFO)效果。如果 s2 不为空,则直接从 s2 操作,避免重复搬运,提高效率。

225. 用队列实现栈

  这道题在我前面java实现队列的文章中也写过,理解思想就不难。代码如下:

class MyStack {

    private Queue<Integer> qu1=new LinkedList<>();
    private Queue<Integer> qu2=new LinkedList<>();

    public MyStack() {   }
    
    public void push(int x) {
        if(!qu1.isEmpty()){
            qu1.offer(x);
        }else if(!qu2.isEmpty()){
            qu2.offer(x);
        }else{
            qu1.offer(x);
        }
    }
    
    public int pop() {
        if(empty()){
            return -1;
        }
        if(!qu1.isEmpty()){
            int size=qu1.size();
            for(int i=0;i<size-1;i++){
                int x=qu1.poll();
                qu2.offer(x);
            }
            return qu1.poll();
        }else{
             int size=qu2.size();
            for(int i=0;i<size-1;i++){
                int x=qu2.poll();
                qu1.offer(x);
            }
            return qu2.poll();
        }
    }
    
    public int top() {
         if(empty()){
            return -1;
        }
        if(!qu1.isEmpty()){
            int size=qu1.size();
            int x=-1;
            for(int i=0;i<size;i++){
                x=qu1.poll();
                qu2.offer(x);
            }
            return x;
        }else{
             int size=qu2.size();
             int x=-1;
            for(int i=0;i<size;i++){
                x=qu2.poll();
                qu1.offer(x);
            }
            return x;
        }
    }
    
    public boolean empty() {
        return qu1.isEmpty()&&qu2.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();
 */

       始终让一个队列存放数据,另一个队列作为辅助。当执行 push 时,把新元素加入当前非空的队列;当执行 poptop 时,将非空队列中的前 n−1 个元素依次出队并加入另一个队列,这样最后留下的元素就是栈顶元素,从而完成出栈或取栈顶操作。