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 作为输出栈,负责出队。当需要 pop 或 peek 时,如果 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 时,把新元素加入当前非空的队列;当执行 pop 或 top 时,将非空队列中的前 n−1 个元素依次出队并加入另一个队列,这样最后留下的元素就是栈顶元素,从而完成出栈或取栈顶操作。