Labels

Saturday, February 7, 2015

Pow(x, n)


Pow(x, n)



Implement pow(x, n).

Naive Way: brute force是O(n)。很明显x^5 = x^2 * x^2 * x 是缩小时间复杂度的关键。这里还要记得有负数次幂的情况。经观察和我第一次做时看别人做法的记忆,7=4+2+1 是核心。
x^7 = x^4 + x^2 + x^1。
那么先将[x^1,x^2,x^4...x^(logn)]罗列出来,
第一次n=7, 7/4 = 1...3 说明有一个x^4,
第二次n=3,    3/2 = 1...1 说明有一个x^2,
第三次n=1,    1/1 = 1...0 说明有一个x^1。
要注意如果商是0,说明没有对应项的乘因子,就不乘或者乘1.0。

这样就变成了不断取最高位的算法。算法复杂度是O(logn),space是O(logn)

public class Solution {
    public double pow(double x, int n) {
        if(n==0 || x==1.0){return 1.0;}
        if(x==-1.0){return n%2==0?1.0:-1.0;}
        if(n < 0){return 1.0/pow(x,-n);}
        int len = (int)Math.floor(Math.log(n)/Math.log(2));
        double rlst = 1.0;
        double[] carry = new double[len+1];
        for(int i = 0;i <= len;i++)
            carry[i] = i==0?x:Math.pow(carry[i-1],2);
        while(n!=0){
            int num = n/(int)Math.pow(2,len);
            rlst *= num==0?1.0:carry[len]*num;
            n %= (int)Math.pow(2,len--);
        }
        return rlst;
    }
}


Improved Way:x^7 = x^4 + x^2 + x^1的这个信息,其实就藏在7这个数的比特位中,7 = 0x0111,
只需要用比特运算就可以提取对应位了。并且,因为这样不需要从高往低乘,可以从低往高乘,那么低位的乘因子乘过以后就不会再用了,不需要一直存着,可以通过与比特位递进同步平方乘因子,达到O(1)space的效果。

这种方法也太牛了,居然只用O(1) run time 和O(1) space。

public class Solution {
    public double pow(double x, int n) {
        if(n < 0){return 1.0/(n==Integer.MIN_VALUE?x*pow(x,-(n+1)):pow(x,-n));}
        double rlst = 1.0;
        while(n!=0){
            if((n & 1) == 1){
                rlst*= x;
            }
            x*=x;
            n = n >> 1;
        }
        return rlst;
    }
}

Merge Intervals


Merge Intervals



 


Given a collection of intervals, merge all overlapping intervals.
For example,
Given [1,3],[2,6],[8,10],[15,18],
return [1,6],[8,10],[15,18].

Naive Way:  排序,然后用greedy的思想不断兼并下一个。

算法复杂度因为排序的原因是O(nlogn)。space是O(n)。

/**
 * Definition for an interval.
 * public class Interval {
 *     int start;
 *     int end;
 *     Interval() { start = 0; end = 0; }
 *     Interval(int s, int e) { start = s; end = e; }
 * }
 */
public class Solution {
    public List<Interval> merge(List<Interval> intervals) {
        if(intervals.size()==0){return intervals;}
        Interval[] arr = new Interval[intervals.size()];
        Comparator<Interval> c = new Comparator<Interval>(){
            public int compare(Interval x, Interval y){
                if(x.start < y.start){
                    return -1;
                }else if(x.start > y.start){
                    return 1;
                }else{
                    if(x.end < y.end){
                        return -1;
                    }else if(x.end > y.end){
                        return 1;
                    }
                }
                return 0;
            }
        };
        for(int i = 0;i < arr.length;i++)
            arr[i] = intervals.get(i);
        Arrays.sort(arr, c);
        intervals.clear();
        int s = arr[0].start;
        int e = arr[0].end;
        int i = 0;
        while(++i < arr.length){
            if(arr[i].start <= e){
                e = Math.max(arr[i].end,e);
            }else{
                Interval interval = new Interval(s,e);
                intervals.add(interval);
                s = arr[i].start;
                e = arr[i].end;
            }
        }
        Interval interval = new Interval(s,e);
        intervals.add(interval);
       
        return intervals;
    }
}

Improved Way: 对于排序的处理应该用更简单的collection.sort,并且排序时可以不管end,因为start一致的时候end无论大小都会被融合进同一个interval里。

 Collections.sort(intervals, new Comparator<Interval>() {
            public int compare(Interval o1, Interval o2) {
                return o1.start - o2.start;
            }
        });

Friday, February 6, 2015

Min Stack


Min Stack



 


Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.
  • push(x) -- Push element x onto stack.
  • pop() -- Removes the element on top of the stack.
  • top() -- Get the top element.
  • getMin() -- Retrieve the minimum element in the stack. 
Naive Way:正常的一个stack的话,取min需要比那里当前的stack重所有元素,当然希望4个function都是O(1)才好。那么每一次push都与当前min比较,再把这些min用指针串起来。着就是一个链表的想法,需要双向链表解决pop后找回前一个的问题。

4个函数都是O(1),运行时间是Java里较快的。

class MinStack {
    class Node{
        int val;
        Node next;
        Node pre; 
        Node last;// pointers to next minimun Node
        Node(int x){
            val = x;
            next = null;
            pre = null;
            last = null;
        }
    }
    
    Node head = new Node(0);
    Node tail = head;
    Node min = null;
    
    public void push(int x) {
        Node node = new Node(x);
        // set stack
        tail.next = node;
        node.pre = tail;
        tail = node;
        // set min
        if(min==null){
            min = node;
        }else{
            if(node.val < min.val){
                node.last = min;
                min = node;
            }else{
                node.last = min;
            }
        }
    }

    public void pop() {
        if(tail==head){
            return;
        }else{
            min = tail.last;
            tail = tail.pre;
        }
    }

    public int top() {
        return tail.val;
    }

    public int getMin() {
        return min==null?0:min.val;
    }
}

Improved Way: Solution里说的是用两个stack,一个正常存放,一个存min,每次push之前先看看是否min和当前被push元素相等,是就同时也push 出min。这样的做法也挺好的。

class MinStack {
    Stack<Integer> stack = new Stack<Integer>();
    Stack<Integer> min = new Stack<Integer>();
    
    public void push(int x) {
        if(min.isEmpty() || (!min.isEmpty() && x <= min.peek()))
            min.push(x);
        stack.push(x);
    }

    public void pop() {
        if(!stack.isEmpty()&& !min.isEmpty()){
            if((int)stack.peek()==(int)min.peek())
                min.pop();
            stack.pop();
        }
    }

    public int top() {
        return stack.isEmpty()?0:stack.peek();
    }

    public int getMin() {
        return min.isEmpty()?0:min.peek();
    }
}



还有一个很有创意的想法是用一个stack,遇到比当前min还小的就Push一次原来的min,pop的时候遇到min时就把当前min变成stack的下一个数,然后push掉这个记号。总体就是利用原stack存当前min。来自leetcode用户sometimescrazy。

class MinStack {
    int min=Integer.MAX_VALUE;
    Stack<Integer> stack = new Stack<Integer>();
    public void push(int x) {
       // only push the old minimum value when the current 
       // minimum value changes after pushing the new value x
        if(x <= min){          
            stack.push(min);
            min=x;
        }
        stack.push(x);
    }

    public void pop() {
       // if pop operation could result in the changing of the current minimum value, 
       // pop twice and change the current minimum value to the last minimum value.
        if(stack.peek()==min) {
            stack.pop();
            min=stack.peek();
            stack.pop();
        }else{
            stack.pop();
        }
        if(stack.empty()){
            min=Integer.MAX_VALUE;
        }
    }

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

    public int getMin() {
        return min;
    }
}

3Sum


3Sum



 


Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note:

  • Elements in a triplet (a,b,c) must be in non-descending order. (ie, abc)
  • The solution set must not contain duplicate triplets.
    For example, given array S = {-1 0 1 2 -1 -4},

    A solution set is:
    (-1, 0, 1)
    (-1, -1, 2) 
 
 
Naive Way:这已经是我第三次做这道题了。 brute force是O(n^3),然后较好的做法是O(n^2)。
经查证,还没有方法能在小于O(n^2)的时间内解决3sum问题。实际在OJ上做,发现即使是同样的O(n^2)
的run time, 也会有超时。

第一种做法, 一开始不排序,先用一个map记录每个数的出现次数,遍历O(n^2)求两两之和,再再map中
看看有没有剩下的那个数,这样的做法需要在形成list的时候排序,并且得有一个set控制duplicate。
但是这样的做法超时了。

public class Solution {

    public List<List<Integer>> threeSum(int[] num) {

        Set<List<Integer>> set = new HashSet<List<Integer>>();

        Map<Integer, Integer> map = new HashMap<Integer, Integer>();

        List<List<Integer>> output = new ArrayList<List<Integer>>();

        for(int i = 0;i < num.length;i++){

            if(map.containsKey(num[i]))

                map.put(num[i], map.get(num[i])+1);

            else

                map.put(num[i], 1);

        }

        for(int i = 0;i < num.length-1;i++){

            for(int j = i+1;j < num.length;j++){

                int a = num[i];

                int b = num[j];

                int c = -num[i]-num[j];

                if(map.containsKey(c)){

                    int count = 1;

                    count += a==c?1:0;

                    count += b==c?1:0;

                    if(count <= map.get(c)){

                        List<Integer> list = new ArrayList<Integer>();

                        int arr[] = new int[3];

                        arr[0] = a;

                        arr[1] = b;

                        arr[2] = c;

                        Arrays.sort(arr);

                        list.add(arr[0]);

                        list.add(arr[1]);

                        list.add(arr[2]);

                        if(!set.contains(list))

                            set.add(list);

                    }

                }

            }

        }

        output.addAll(set);

        return output;

    }

    

}

 
 
Improved Way: 
第二种做法,也是比较牛一点的做法,就是wiki上给的做法,但是这里要求去除重复,要做出修改。

public class Solution {
    public List<List<Integer>> threeSum(int[] num) {
        List<List<Integer>> rlst = new ArrayList<List<Integer>>();
        Arrays.sort(num);
        for(int u = 0;u < num.length-2;u++){
            if(u==0 || (u > 0 && num[u]!=num[u-1])){
            int i = u+1;
            int j = num.length-1;
            while(i < j){
                int sum = num[i]+num[j]+num[u];
                if(sum==0){
                    List<Integer> list = new ArrayList<Integer>();
                    list.add(num[u]);
                    list.add(num[i]);
                    list.add(num[j]);
                    rlst.add(list);
                    while(i < j){
                        if(num[j]!=num[j-1])
                            break;
                        j--;
                    }
                    while(i < j){
                        if(num[i]!=num[i+1])
                            break;
                        i++;
                    }
                    i++;
                    j--;
                }else if(sum > 0){
                    j--;
                }else{
                    i++;
                }
            }
            }
        }
        return rlst;
    }
}


最后我发现我第一次的做法,是可以通过的O(n^2)非常非常的厉害,我觉得,居然想到了用正数负数。

public class Solution {
    List<List<Integer>> output;
    HashSet<List<Integer>> visited;
    public List<List<Integer>> threeSum(int[] num) {
        output = new ArrayList<List<Integer>>();
        visited = new HashSet<List<Integer>>();
        //Arrays.sort(num);
        Map<Integer, Integer> positive = new HashMap<Integer, Integer>();
        Map<Integer, Integer> negative = new HashMap<Integer, Integer>();
        int numOfZeros = 0;
        // construct the mapping
        for(int i = 0;i < num.length;i++){
            if(num[i] == 0)
                numOfZeros++;
            if(num[i] > 0)
                if(positive.containsKey(num[i]))
                    positive.put(num[i], positive.get(num[i])+1);
                else
                    positive.put(num[i],1);
            if(num[i] < 0)
                if(negative.containsKey(num[i]))
                    negative.put(num[i], negative.get(num[i])+1);
                else
                    negative.put(num[i],1);
        }
       
        // generate results
        // two positive + one negative
        for(Map.Entry<Integer, Integer> a:positive.entrySet())
            for(Map.Entry<Integer, Integer> b:positive.entrySet())
                if(a.getKey() != b.getKey() || a.getValue() >= 2)
                    if(negative.containsKey(-a.getKey()-b.getKey()))
                        addResult(a.getKey(),b.getKey(),-a.getKey()-b.getKey());
                   
       
        // two negative + one positive
        for(Map.Entry<Integer, Integer> a:negative.entrySet())
            for(Map.Entry<Integer, Integer> b:negative.entrySet())
                if(a.getKey() != b.getKey() || a.getValue() >= 2)
                    if(positive.containsKey(-a.getKey()-b.getKey()))
                        addResult(a.getKey(),b.getKey(),-a.getKey()-b.getKey());
       
        // one positive+zero+one negative
        if(numOfZeros > 0)
            for(Map.Entry<Integer, Integer> a:negative.entrySet())
                if(positive.containsKey(-a.getKey()))
                    addResult(0,a.getKey(),-a.getKey());
       
        // three zeros
        if(numOfZeros > 2)
            addResult(0,0,0);
           
        return output;
    }
   
    private void addResult(int a, int b, int c){
        int array[] = {a,b,c};
        Arrays.sort(array);
        List<Integer> result = new ArrayList<Integer>();
        result.add(array[0]);
        result.add(array[1]);
        result.add(array[2]);
        if(!visited.contains(result)){
            visited.add(result);
            output.add(result);
        }
    }
}

 

Clone Graph


Clone Graph



 


Clone an undirected graph. Each node in the graph contains a label and a list of its neighbors.

OJ's undirected graph serialization: Nodes are labeled uniquely.
We use # as a separator for each node, and , as a separator for node label and each neighbor of the node. As an example, consider the serialized graph {0,1,2#1,2#2,2}.
The graph has a total of three nodes, and therefore contains three parts as separated by #.
  1. First node is labeled as 0. Connect node 0 to both nodes 1 and 2.
  2. Second node is labeled as 1. Connect node 1 to node 2.
  3. Third node is labeled as 2. Connect node 2 to node 2 (itself), thus forming a self-cycle.
Visually, the graph looks like the following:
       1
      / \
     /   \
    0 --- 2
         / \
         \_/ 
 
 
Naive Way:难点在于穿件了新的节点如何找回原来对应的节点,并且输入只有一个节点。
用Map可以正中下怀的解决这个问题。
 
算法复杂度是O(n),space是O(n)。 
 
/**

 * Definition for undirected graph.

 * class UndirectedGraphNode {

 *     int label;

 *     List<UndirectedGraphNode> neighbors;

 *     UndirectedGraphNode(int x) { label = x; neighbors = new ArrayList<UndirectedGraphNode>(); }

 * };

 */

public class Solution {

    public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) {

        Map<UndirectedGraphNode, UndirectedGraphNode> map = new HashMap<UndirectedGraphNode, UndirectedGraphNode>();

        Set<UndirectedGraphNode> set = new HashSet<UndirectedGraphNode>();

        Queue<UndirectedGraphNode> queue = new LinkedList<UndirectedGraphNode>();

        if(node==null){return null;}

        // BFS, give every node a clone, put relationship into a map

        UndirectedGraphNode newHead = new UndirectedGraphNode(node.label);

        map.put(node,newHead);

        queue.add(node);

        while(!queue.isEmpty()){

            UndirectedGraphNode temp = queue.poll();

            for(int i = 0;i < temp.neighbors.size();i++){

                if(!map.containsKey(temp.neighbors.get(i))){

                    queue.add(temp.neighbors.get(i));

                    UndirectedGraphNode newNode = new UndirectedGraphNode(temp.neighbors.get(i).label);

                    map.put(temp.neighbors.get(i),newNode);

                }

            }

        }

        // according to the map, construct new graph

        queue.add(node);

        set.add(node);

        while(!queue.isEmpty()){

            UndirectedGraphNode temp = queue.poll();

            if(!map.containsKey(temp)){return null;}

            UndirectedGraphNode clone = map.get(temp);

            for(int i = 0;i < temp.neighbors.size();i++){

                if(!map.containsKey(temp.neighbors.get(i))){return null;}

                clone.neighbors.add(map.get(temp.neighbors.get(i)));

                if(!set.contains(temp.neighbors.get(i))){

                    set.add(temp.neighbors.get(i));

                    queue.add(temp.neighbors.get(i));

                }

            }

        }

        return map.containsKey(node)?map.get(node):null;

    }

}
 

 



Improves Way: 后来,我根据Copy list with random pointer里看别人的一个算法,想到可以套用过来,在每隔节点的最后增加一个新的neighbor,然后将每个节点的最后一个neighbor指向他所有neighbor的最后一个neighbor。



 



这样子不需要额外的空间存关系,但是遍历的原因还是要O(n)的space。



 



public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) {
        if(node == null){return null;}
       
        // create a new Node with the same label put it as the last neighbor for the original node
        ArrayList<Integer> table = new ArrayList<Integer>();
        UndirectedGraphNode cur = node;
        UndirectedGraphNode rslt = null;
        Stack<UndirectedGraphNode> s = new Stack<UndirectedGraphNode>();
        s.push(node);
        while(!s.isEmpty()){
            cur = s.pop();
            if(!table.contains(cur.label)){
                table.add(cur.label);
                for(int i = 0;i < cur.neighbors.size();i++){
                    if(!table.contains(cur.neighbors.get(i).label)){
                        s.push(cur.neighbors.get(i));
                    }
                }
                UndirectedGraphNode newNode = new UndirectedGraphNode(cur.label);
                cur.neighbors.add(newNode);
            }
        }
       
        // assign relationship for the new nodes
        UndirectedGraphNode temp = null;
        table.clear();
        s.push(node);
        while(!s.isEmpty()){
            cur = s.pop();
            if(!table.contains(cur.label)){
                table.add(cur.label);
                for(int i = 0;i < cur.neighbors.size()-1;i++){
                    temp = cur.neighbors.get(i);
                    cur.neighbors.get(cur.neighbors.size()-1).neighbors.add(temp.neighbors.get(temp.neighbors.size()-1));
                    s.push(temp);
                }
            }
        }
       
        rslt = node.neighbors.get(node.neighbors.size()-1);
       
        // delete the relationship between original nodes and new nodes
        table.clear();
        s.push(node);
        while(!s.isEmpty()){
            cur = s.pop();
            if(!table.contains(cur.label)){
                table.add(cur.label);
                cur.neighbors.remove(cur.neighbors.size() - 1);
                for(int i = 0;i < cur.neighbors.size();i++){
                    s.push(cur.neighbors.get(i));
                }
            }
        }
       
        return rslt;
    }
 



 

3Sum Closest


3Sum Closest



 


Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target. Return the sum of the three integers. You may assume that each input would have exactly one solution.
    For example, given array S = {-1 2 1 -4}, and target = 1.

    The sum that is closest to the target is 2. (-1 + 2 + 1 = 2). 
 
 
Naive Way: 我一开始想了一个binary search 的办法,先把数组排序,每次取一头(i)一尾(j),在中间
找最接近x = target-num[i]-num[j]的数,如果得到的和num[i]+num[j]+num[x]小于target,说明
要增加sum,i++,否则就要减小sum,j--。这个方法只需要O(nlogn)的时间。
 
public class Solution {
    public int threeSumClosest(int[] num, int target) {
        Arrays.sort(num);
        int min = Integer.MAX_VALUE;
        int rlst = target;
        int i = 0, j = num.length-1;
        while(i+1 < j){
            int rest = target-num[i]-num[j];
            int x = binarySearch(num, i+1, j-1, rest);
            int sum = num[i]+num[j]+num[x];
            if(sum==target){return target;}
            if(sum < target)
                i++;
            else
                j--;
            if(Math.abs(sum-target) < min){
                min = Math.abs(sum-target);
                rlst = sum;
            }
        }
        return rlst;
    }   

    private int binarySearch(int num[], int begin, int end, int t){
        int middle = begin;
        if(t > num[end]){return end;}
        if(t < num[begin]){return begin;}
        while(begin < end){
            middle = (begin+end)/2;
            if(num[middle]==t)
                break;
            if(num[middle] < t)
                begin = middle+1;
            if(num[middle] > t)
                end = middle-1;
        }
        return Math.abs(num[middle]-t) >= Math.abs(num[middle+1]-t)?middle+1:middle;
    }
} 


这个方法可以通过OJ,但是这个方法是错的,在Discuss上居然有人跟我想了同一个方法,并且有别人提出了质疑,举出了
[0 5 50 100 140] 150, 这个例子, 这个例子说明了即使当前和小于target,也不一定要增加序数而有可能要减小序数,因为前后两次binary search得到的数会不一样。于是这个O(nlogn)的方法宣告失败。

Improved Way:从头开始想,brute force 需要O(n^3),那么应该在O(n^2)或者O(n^2 logn)内解决。3-sum就可以O(n^2)解决,但是这次不能确切找一个数,用set先存起来没什么用作。根据之前的方案,通过O(n^2)可以遍历所有两个数的组合,那么可以通过O(logn)的 binary search找最接近的数,这样下来算法就是O(n^2 logn),这样的方法我试了试,是可以通过的,而且运行时间并不慢。

public class Solution {
    public int threeSumClosest(int[] num, int target) {
        Arrays.sort(num);
        int min = Integer.MAX_VALUE;
        int rlst = target;
        for(int i = 0;i < num.length-2;i++){
            for(int j = num.length-1; j >= i+2;j--){
                int rest = target-num[i]-num[j];
                int x = binarySearch(num, i+1, j-1, rest);
                int sum = num[i]+num[j]+num[x];
                if(Math.abs(sum-target) < min){
                    min = Math.abs(sum-target);
                    rlst = sum;
                }
            }
        }
        return rlst;
    }
    
    private int binarySearch(int num[], int begin, int end, int t){
        int middle = begin;
        if(t > num[end]){return end;}
        if(t < num[begin]){return begin;}
        while(begin < end){
            middle = (begin+end)/2;
            if(num[middle]==t)
                break;
            if(num[middle] < t)
                begin = middle+1;
            if(num[middle] > t)
                end = middle-1;
        }
        return (begin+end)/2;
    }
}


Discuss中有唯一一种O(n^2)的方法,用了3sum一模一样的策略。原来觉得很厉害,现在觉得好像谁都会了。


public class Solution {
    public int threeSumClosest(int[] num, int target) {
        if(num.length < 3){return 0;}
        int closest = num[0]+num[1]+num[2];
        int dis = Math.abs(closest-target);
        Arrays.sort(num);
        for(int i = 0;i < num.length-2;i++){
            // set two pointers, one from small-end, one from large-end
            int p = i+1;
            int q = num.length-1;
            while(p < q){
                int sum = num[i]+num[p]+num[q];
                if(Math.abs(sum-target) < dis){
                    closest = sum;
                    dis = Math.abs(sum-target);
                }
                if(sum > target)
                    q--;
                if(sum < target)
                    p++;
                if(sum == target)
                    return sum;
            }
        }
        return closest;
    }
}



 



 

Tuesday, February 3, 2015

Implement strStr()


Implement strStr()





Implement strStr().
Returns the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack.

Naive Way:从一堆稻草中找一根针,这简直比大海捞针还难,因为稻草跟针长得很像。是否可以直接比较呢,对每一个字符都遍历一遍,这样的算法复杂度是O(n^2)。因为我之前做过,我貌似知道这样做虽然很傻,但是可以通过。


public class Solution {
    public int strStr(String haystack, String needle) {
        for(int i = 0;i <= haystack.length()-needle.length();i++){
            int j = 0;
            for(;j < needle.length();j++)
                if(haystack.charAt(i+j)!=needle.charAt(j))
                    break;
            if(j == needle.length())
                return i;
        }
        return -1;
    }
}

 



 


Improved Way:怎能满足于如此傻的方法呢,这个世界一定有比这个更好的方法才对。是的,那就是KMP算法了,专门用于从稻草中捞出一根针的算法。

 我看了一下维基百科的例子,觉得很好理解,于是根据例子自己写了一个算法。下面是那个例子的详解

reference to http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm

To illustrate the algorithm's details, we work through a (relatively artificial) run of the algorithm, where W = "ABCDABD" and S = "ABC ABCDAB ABCDABCDABDE". At any given time, the algorithm is in a state determined by two integers:
  • m, denoting the position within S where the prospective match for W begins,
  • i, denoting the index of the currently considered character in W.
In each step we compare S[m+i] with W[i] and advance if they are equal. This is depicted, at the start of the run, like
             1         2  
m: 01234567890123456789012
S: ABC ABCDAB ABCDABCDABDE
W: ABCDABD
i: 0123456

We proceed by comparing successive characters of W to "parallel" characters of S, moving from one to the next if they match. However, in the fourth step, we get S[3] = ' ' and W[3] = 'D', a mismatch. Rather than beginning to search again at S[1], we note that no 'A' occurs between positions 0 and 3 in S, except at 0; hence, having checked all those characters previously, we know that there is no chance of finding the beginning of a match if we check them again. Therefore, we move on to the next character, setting m = 4 and i = 0.
             1         2  
m: 01234567890123456789012
S: ABC ABCDAB ABCDABCDABDE
W:     ABCDABD
i:     0123456

We quickly obtain a nearly complete match "ABCDAB" when, at W[6] (S[10]), we again have a discrepancy. However, just prior to the end of the current partial match, we passed an "AB", which could be the beginning of a new match, so we must take this into consideration. As we already know that these characters match the two characters prior to the current position, we need not check them again; we simply reset m = 8, i = 2 and continue matching the current character. Thus, not only do we omit previously matched characters of S, but also previously matched characters of W.
             1         2  
m: 01234567890123456789012
S: ABC ABCDAB ABCDABCDABDE
W:         ABCDABD
i:         0123456

This search fails immediately, however, as the pattern still does not contain a space, so as in the first trial, we return to the beginning of W and begin searching at the next character of S: m = 11, reset i = 0.
             1         2  
m: 01234567890123456789012
S: ABC ABCDAB ABCDABCDABDE
W:            ABCDABD
i:            0123456

Once again, we immediately hit upon a match "ABCDAB", but the next character, 'C', does not match the final character 'D' of the word W. Reasoning as before, we set m = 15, to start at the two-character string "AB" leading up to the current position, set i = 2, and continue matching from the current position.
             1         2  
m: 01234567890123456789012
S: ABC ABCDAB ABCDABCDABDE
W:                ABCDABD
i:                0123456

This time we are able to complete the match, whose first character is S[15].

我的理解就是在匹配当前Index的同时,留一个心眼找从中间开始的和needle匹配的match,然后给它一个index记录这个中间开始的匹配的位置(在needle上的位置),一旦主线匹配失败,就转到支线匹配,同时,要把主线和支线的地位交换,支线成为了主线,旧的主线去记录新的中间可能出现的匹配。

这个算法的复杂度是O(n)。

public class Solution {
    public int strStr(String haystack, String needle) {
        int p = 0; // for haystack
        int i = 0, j = 0; // for needle
        int state = 0; // state=0 means i being compared, state=1 means j is being compared
        while(p < haystack.length() && i < needle.length() && j < needle.length()){
            switch(state){
                case 0:
                    if(i!= 0){
                        // if a new match start in the middle
                        if(haystack.charAt(p)==needle.charAt(j))
                            j++;
                        else if(haystack.charAt(p)==needle.charAt(0))
                            j = 1;
                        else
                            j = 0;
                    }
                    if(haystack.charAt(p)==needle.charAt(i)){
                        i++;
                    }else{
                        i = 0;
                        state = 1;
                    }
                    break;
                case 1:
                    if(j!=0){
                        // if a new match start in the middle
                        if(haystack.charAt(p)==needle.charAt(i))
                            i++;
                        else if(haystack.charAt(p)==needle.charAt(0))
                            i = 1;
                        else
                            i = 0;
                    }
                    if(haystack.charAt(p)==needle.charAt(j)){
                        j++;
                    }else{
                        j = 0;
                        state = 0;
                    }
                    break;
                default:
                    break;
            }
            p++;
            // if a total match is caught
            if(i==needle.length() || j==needle.length())
                return p-needle.length();
        }
        return needle.length()==0?0:-1;
    }
}

这个方法可以在OJ上通过,但是是错误的。因为我只存了两个指针,当出现第三个和开始一致的情况,就会出错。按照这个思路需要存贮n个指针,轮流替换,这样的话每次一个指针更新,其他所有指针都要遍历一遍,算法复杂度就成了O(n^2),真是个烂算法。

自己写KMP的流程。
首先 ,构建一个针对于needle的table,这个table要记录一旦haystack[m+i] 与 needle[i] 不匹配时,m要返回到哪里,i要返回到哪里。

示例:
index    0   1   2   3  4   5   6  
needle  A  B  D  A  B  C  D
table    -1  0   0   0   1  2   0

比如匹配到i = 5的C处,发现不匹配,那么haystack的指针就要减少2个,needle的指针就要从2开始匹配,从2开始匹配这点显而易见,因为都已经匹配到C了,说明前两个一定是AB,haystack的当前位置肯定不是C,那么看看是不是之前具有相同pattern AB的下一个位置处的D,同时,因为之前是用[m+i]来记录haystack的指针的,此时needle已经是从第2个开始了,需要保持[m+i]中的 i=2, 而之前是i=5,那么m就要相应的增加3 (因为i减少了3)。

根据例子可以依照规律构建table。

算法复杂度是O(n+m), space是O(m)

public class Solution {
    public int strStr(String haystack, String needle) {
        // KMP
        if(needle.length() == 0){return 0;}
        int t[] = new int[needle.length()];
        // construct table
        t[0] = -1;
        for(int i = 1;i < t.length;i++){
            if(i==1){t[i] = 0;}
            else{
                if(needle.charAt(i-1)==needle.charAt(t[i-1])){
                    t[i] = t[i-1]+1;
                }else{
                    t[i] = 0;
                }
            }
        }
        // kmp search
        int m = 0, i = 0;
        while(m <= haystack.length()-needle.length()){
            if(haystack.charAt(m+i)==needle.charAt(i)){
                i++;
            }else{
                if(i > 0){
                    m = m+i-t[i];
                    i = t[i];
                }else{
                    m++;
                }
            }
            if(i==needle.length())
                return m;
        }
        return -1;
    }
}