Wednesday, June 28, 2017

403. Frog Jump

Version #3 HashMap of stones and Keep recording the failed tries
Give the k of last step, we know now we can only jump k-1, k, k+1
current position x + k-1/k/k+1 is not a valid stone position, then no need to explore
Pruning -> given a position x, if we have tried that there's no way for us to read the end given a k, then next time we come to x again, we know that we don't need to explore x with a k again
So I maintained a Map<Integer, Set<Integer>> whose key is position x, and value is set of invalid k

Time O(3^step) ??
Space O(nk) ??

79.15 %
class Solution {
    public boolean canCross(int[] stones) {
Set<Integer> stoneSet = new HashSet<>();
Map<Integer, Set<Integer>> failMap = new HashMap<>();
for (int stone : stones) {
stoneSet.add(stone);
failMap.put(stone, new HashSet<>());
}
if (!stoneSet.contains(1)) {
return false;
}
return dfs(failMap, stoneSet, 1, 1, stones[stones.length - 1]);
}

private boolean dfs(Map<Integer, Set<Integer>> failMap, Set<Integer> stoneSet, int x, int k, int target) {
if (x >= target) {
return true;
}
for (int nextK = k - 1; nextK <= k + 1; nextK++) {
if (nextK > 0 && stoneSet.contains(x + nextK)) {
if (!failMap.get(x).contains(nextK) && dfs(failMap, stoneSet, x + nextK, nextK, target)) {
return true;
} else {
failMap.get(x).add(nextK);
}
}
}
return false;
}
}


Version #2 HashMap of steps
二刷
感觉问题很直观,之前的Version#1思路挺有意思的,回头再看看
给每一个石头的position维护一个以position为key,能够往前走的steps set 为value的hashmap
每次走到一个石头,遍历它的set,如果从当前pos + k 能走到另外一个石头(hashmap contains key),那么就在能走到的那个石头的set里面加上k, k - 1, k + 1
如果pos + k恰好等于最后一块石头的pos,就直接返回true

一个bug是一直有 concurrent exception
问题大概出在对于set同时遍历又同时add
本来以为要一个iterator
后来发现在代码里加上 if (k > 1) {  set.add(k - 1) } 的限制条件就行了
这样保证正在iterate 的set 和之后add的set不会是同一个set
又仔细想了想,这个问题主要是针对 j = 0 (pos = 0) 和j = 1 (pos = 1)的时候两个edge case
走0步的时候会同时编辑正在iterate的set
62.82 %

class Solution {
    public boolean canCross(int[] stones) {
        if (stones == null || stones.length == 0) return false;
        if (stones.length == 1) return true;
        if (stones[1] > 1) return false;
        // key-stone position, value-set of possible steps
        Map<Integer, Set<Integer>> steps = new HashMap<>();
        int length = stones.length;
        for (int i = 0; i < length; i++) {
            steps.put(stones[i], new HashSet<>());
        }
        steps.get(0).add(1);
        Set<Integer> currKSet = null;
        Set<Integer> nextKSet = null;
        for (int j = 0; j < length - 1; j++) {
            for (int k : steps.get(stones[j])) {
                int next = stones[j] + k;
                if (next == stones[length - 1]) return true;
             
                nextKSet = steps.get(next);
                if (nextKSet != null) {
                    if (k > 1) {
                        nextKSet.add(k - 1);
                    }
                    nextKSet.add(k);
                    nextKSet.add(k + 1);
                }
            }
        }
        return false;
    }
}


Version #1
一个时间复杂度稍微有点高的方法,每次从position开始向后看所有的stones,如果小于dist就继续向后看,如果大于dist就break
这样当k很大的时候,相当于做了很多无用的搜索
优化方法是维护一个hashmap key是dist from start point,value是index
这样可以直接搜索出距离当前position 的距离为k-1, k, k+1的stone是否存在
优化有空再写

关键是要用memorization进行pruning,否则会TLE
因为这里对于每一个stone,k是不连续的,所以此处要对k用hashmap

所以是一个list of hashmap,每一个stone里面有一个相应的以k为key,canCross为value的hashmap
80.22 %
public class Solution {
    public boolean canCross(int[] stones) {
        if (stones == null) throw new IllegalArgumentException();
        if (stones.length <= 1) return true;
        //must jump 1 step
        if (stones[1] > 1) return false;
     
        //for every stone, there's a hash map
        //key-k(last step), value-canCross
        List<Map<Integer, Boolean>> visited = new ArrayList<>();
        for (int i = 0; i < stones.length; i++) {
            visited.add(new HashMap<Integer, Boolean>());
        }
     
        //Standing at the first stone && had jumped 1 step
        return canCross(stones, 1, 1, visited);
    }
    //Standing at each position, the frog can jump to the stone in k-1/k/k+1 units, k is the jumpint distance of the last step
    private boolean canCross(int[] stones, int position, int k, List<Map<Integer, Boolean>> visited) {
        Map<Integer, Boolean> visitedMap = visited.get(position);
        if (visitedMap.containsKey(k)) return visitedMap.get(k);
     
     
        if (position == stones.length - 1) return true;
        for (int nextPosition = position + 1; nextPosition < stones.length; nextPosition++) {
            int dist = stones[nextPosition] - stones[position];
            if (dist < k - 1) continue;
            if (dist > k + 1) break;
            //k-1 <= dist <= k+1
            if (canCross(stones, nextPosition, dist, visited)) {
                visitedMap.put(k, true);
                return true;
            }
        }
        visitedMap.put(k, false);
        return false;
    }
}

No comments:

Post a Comment