Minimum Adjustment Cost

Online Judge

Given an integer array, adjust each integers so that the difference of every adjacent integers are not greater than a given number target.

If the array before adjustment is A, the array after adjustment is B, you should minimize the sum of |A[i]-B[i]|

Example

Given [1,4,2,3] and target = 1, one of the solutions is [2,3,2,3], the adjustment cost is 2 and it’s minimal.

Return 2.

Note

You can assume each number in the array is a positive integer and not greater than 100.

Solution: DP

state: f[i][v] 前i个数,第i个数调整为v,满足相邻两数<=target,所需要的最小代价

function: f[i][v] = min(f[i-1][v’] + |A[i]-v|, |v-v’| <= target)

Time Complexity: O(m*n*t) m: array size n:最大可能的数 t:required range

Space Complexity: O(m*n)

 

public int MinAdjustmentCost(ArrayList<Integer> A, int target) {
    if (A == null || A.size() <= 1) {
        return 0;
    }
    int maxInt = 100;//assume each number in the array is a positive integer and not greater than 100
    int[][] minCost = new int[A.size() + 1][maxInt + 1];
    for (int i = 0; i <= maxInt; i++) {
        minCost[0][i] = 0;
    }
    for (int i = 1; i <= A.size(); i++) {
        for (int j = 0; j <= maxInt; j++) {
            int costAiToJ = Math.abs(A.get(i - 1) - j);
            int min = Integer.MAX_VALUE;
            //|k-j|<=target
            //max(0, j-target) <= k <= min(target+j, maxInt)
            for (int k = Math.max(0, j - target); k <= Math.min(target + j, maxInt); k++) {
                if (minCost[i - 1][k] + costAiToJ < min) {
                    min = minCost[i - 1][k] + costAiToJ;
                }
            }
            minCost[i][j] = min;
        }
    }
    int finalMinCost = minCost[A.size()][0];
    for (int i = 0; i <= maxInt; i++) {
        if (minCost[A.size()][i] < finalMinCost) {
            finalMinCost = minCost[A.size()][i];
        }
    }
    return finalMinCost;
}
FacebookTwitterGoogle+Share

Backpack I&II

BackPack I

– different sizes same price, ask maximum solution with fixed bag size

Given n items with size Ai, an integer m denotes the size of a backpack. How full you can fill this backpack?

 Example

If we have 4 items with size [2, 3, 5, 7], the backpack size is 11, we can select [2, 3, 5], so that the max size we can fill this backpack is 10. If the backpack size is 12. we can select [2, 3, 7] so that we can fulfill the backpack.

You function should return the max size we can fill in the given backpack.

Note

You can not divide any item into small pieces.

Challenge

O(n x m) time and O(m) memory.

O(n x m) memory is also acceptable if you do not know how to optimize memory.

 

Solution 1: DP – O(nm) time and O(nm) memory

state: f[i][j] = “前i”个数,取出一些组成和为<=j的最大值

function: f[i][j] = f[i-1][j – a[i]] + a[i] //取a[i]的情况

o f[i-1][j] //不取a[i]的情况

两种情况取max

public int backPack(int m, int[] A) {
    if (m <= 0 || A == null || A.length == 0) {
        return 0;
    }
    int[][] backPack = new int[A.length + 1][m + 1];
    for (int i = 0; i <= A.length; i++) {
        backPack[i][0] = 0;
    }
    for (int j = 0; j <= m; j++) {
        backPack[0][j] = 0;
    }
    int max = 0;
    for (int i = 1; i <= A.length; i++) {
        for (int j = 1; j <= m; j++) {
            //doesn't contain A[i]
            backPack[i][j] = backPack[i - 1][j];
            //contains A[i]
            if (j >= A[i - 1]) {
                backPack[i][j] = Math.max(backPack[i][j], backPack[i - 1][j - A[i - 1]] + A[i - 1]);
            }
            if (backPack[i][j] > max) {
                max = backPack[i][j];
            }
        }
    }
    return max;
}

Solution 2. O(mn) time O(n) space, 利用行数取模优化空间 rolling array

public int backPack(int m, int[] A) {
    if (m <= 0 || A == null || A.length == 0) {
        return 0;
    }
    int[][] backPack = new int[2][m + 1];
    for (int i = 0; i < 2; i++) {
        backPack[i][0] = 0;
    }
    for (int j = 0; j <= m; j++) {
        backPack[0][j] = 0;
    }
    int max = 0;
    for (int i = 1; i <= A.length; i++) {
        for (int j = 1; j <= m; j++) {
            //doesn't contain A[i]
            backPack[i % 2][j] = backPack[(i - 1) % 2][j];
            //contains A[i]
            if (j >= A[i - 1]) {
                backPack[i % 2][j] = Math.max(backPack[i % 2][j], backPack[(i - 1) % 2][j - A[i - 1]] + A[i - 1]);
            }
            if (backPack[i % 2][j] > max) {
                max = backPack[i % 2][j];
            }
        }
    }
    return max;
}

Solution 3: dp

f[i][j] = 前i个item能不能正好组成j

f[i][j] = f[i-1][j-a[i]] //取a[i]

f[i-1][j] //不取a[i]

两者取或

return max j which f[i][j] == true

public int backPack(int m, int[] A) {
    if (m <= 0 || A == null || A.length == 0) {
        return 0;
    }
    boolean[][] backPack = new boolean[A.length + 1][m + 1];
    backPack[0][0] = true;
    for (int i = 1; i <= A.length; i++) {
        backPack[i][0] = true;
    }
    for (int j = 1; j <= m; j++) {
        backPack[0][j] = false;
    }
    int max = 0;
    for (int i = 1; i <= A.length; i++) {
        for (int j = 1; j <= m; j++) {
            backPack[i][j] = backPack[i - 1][j];
            if (j >= A[i - 1]) {
                backPack[i][j] |= backPack[i - 1][j - A[i - 1]];
            }
            if (backPack[i][j]) {
                max = j;
            }
        }
    }
    return max;
}

BackPack II

– different size and price, ask for maximum value with fixed bag size

Solution 1. O(m*n) time, O(m*n) space

f[i][j]:maximum value with first i items in A with a m sized bag

f[i][j] = f[i-1][j – a[i]] + v[i] //取a[i]的情况

= f[i-1][j] //不取a[i]的情况

两者取max, return f[n][m]

public int backPackII(int m, int[] A, int V[]) {
    if (A == null || V == null || m <= 0) {
        return 0;
    }
    int[][] maxValue = new int[A.length + 1][m + 1];
    for (int i = 0; i <= A.length; i++) {
        maxValue[i][0] = 0;
    }
    for (int j = 0; j <= m; j++) {
        maxValue[0][j] = 0;
    }
    for (int i = 1; i <= A.length; i++) {
        for (int j = 1; j <= m; j++) {
            maxValue[i][j] = maxValue[i - 1][j];
            if (j >= A[i - 1]) {
                maxValue[i][j] = Math.max(maxValue[i][j], maxValue[i - 1][j - A[i - 1]] + V[i - 1]);
            }
        }
    }
    return maxValue[A.length][m];
}

Solution 2: O(m*n) time, O(m) space

利用rolling array 进行优化

public int backPackII(int m, int[] A, int V[]) {
    if (A == null || V == null || m <= 0) {
        return 0;
    }
    int[][] maxValue = new int[2][m + 1];
    for (int i = 0; i < 2; i++) {
        maxValue[i][0] = 0;
    }
    for (int j = 0; j <= m; j++) {
        maxValue[0][j] = 0;
    }
    for (int i = 1; i <= A.length; i++) {
        for (int j = 1; j <= m; j++) {
            maxValue[i % 2][j] = maxValue[(i - 1) % 2][j];
            if (j >= A[i - 1]) {
                maxValue[i % 2][j] = Math.max(maxValue[i % 2][j], maxValue[(i - 1) % 2][j - A[i - 1]] + V[i - 1]);
            }
        }
    }
    return maxValue[A.length % 2][m];
}

Wildcard Matching

Implement wildcard pattern matching with support for '?' and '*'.

  • '?' Matches any single character.
  • '*' Matches any sequence of characters (including the empty sequence).

The matching should cover the entire input string (not partial).

 Example
<code>isMatch("aa","a") → false
isMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "*") → true
isMatch("aa", "a*") → true
isMatch("ab", "?*") → true
isMatch("aab", "c*a*b") → false</code>

Solution1. Dynamic Programming

isMatch[i][j] = if first i chars for s can match first j chars of p

j==’*’: OR(isMatch[0~i][j-1])

j==’?’: isMatch[i-1][j-1]

else: isMatch[i-1][j-1] && s.charAt(i-1)==p.charAt(j-1)

public boolean isMatch(String s, String p) {
    //dp version
    if (s == null || p == null) {
        return false;
    }
    boolean[][] isMatch = new boolean[s.length() + 1][p.length() + 1];
    isMatch[0][0] = true;
    for (int i = 1; i <= s.length(); i++) {
        isMatch[i][0] = false;
    }
    for (int j = 1; j <= p.length(); j++) {
        isMatch[0][j] = isMatch[0][j - 1] && p.charAt(j - 1) == '*';
    }
    for (int i = 1; i <= s.length(); i++) {
        for (int j = 1; j <= p.length(); j++) {
            if (p.charAt(j - 1) == '*') {
                for (int k = 0; k <= i; k++) {
                    if (isMatch[k][j - 1]) {
                        isMatch[i][j] = true;
                        break;
                    }
                }
            } else if (p.charAt(j - 1) == '?') {
                isMatch[i][j] = isMatch[i - 1][j - 1];
            } else {
                isMatch[i][j] = isMatch[i - 1][j - 1] && s.charAt(i - 1) == p.charAt(j - 1);
            }
        }
    }
    return isMatch[s.length()][p.length()];
}

Interleaving String

Given three strings: s1, s2, s3, determine whether s3 is formed by the interleaving of s1 and s2.

Example

For s1 = "aabcc", s2 = "dbbca"

  • When s3 = "aadbbcbcac", return true.
  • When s3 = "aadbbbaccc", return false.

 

Solution: Dynamic Programming
isInterleave[i][j] = if first i chars of s1 and first j chars of s2 is interleave of first i+j chars of s3
isInterleave[i][j] = isInterleave[i – 1][j] && s1.charAt(i – 1) == s3.charAt(i + j – 1))
|| (isInterleave[i][j – 1] && s2.charAt(j – 1) == s3.charAt(i + j – 1))

public boolean isInterleave(String s1, String s2, String s3) {
    if (s1 == null || s2 == null || s3 == null
            || s1.length() + s2.length() != s3.length()) {
        return false;
    }
    //isInterleave[i][j] = if first i chars of s1 and first j chars of s2
    //is interleave of first i+j chars of s3
    boolean[][] isInterleave = new boolean[s1.length() + 1][s2.length() + 1];
    isInterleave[0][0] = true;
    for (int i = 1; i <= s1.length(); i++) {
        isInterleave[i][0] = isInterleave[i - 1][0] && s1.charAt(i - 1) == s3.charAt(i - 1);
    }
    for (int j = 1; j <= s2.length(); j++) {
        isInterleave[0][j] = isInterleave[0][j - 1] && s2.charAt(j - 1) == s3.charAt(j - 1);
    }
    for (int i = 1; i <= s1.length(); i++) {
        for (int j = 1; j <= s2.length(); j++) {
            isInterleave[i][j] = (isInterleave[i - 1][j] && s1.charAt(i - 1) == s3.charAt(i + j - 1))
                                 || (isInterleave[i][j - 1] && s2.charAt(j - 1) == s3.charAt(i + j - 1));
        }
    }
    return isInterleave[s1.length()][s2.length()];
}

Longest Common Subsequence

Given two strings, find the longest common subsequence (LCS).

Your code should return the length of LCS.

Example

For "ABCD" and "EDCA", the LCS is "A" (or "D", "C"), return 1.

For "ABCD" and "EACB", the LCS is "AC", return 2.

Clarification

What’s the definition of Longest Common Subsequence?

Solution: Dynamic Programming

public int longestCommonSubsequence(String A, String B) {
    if (A == null || A.length() == 0 || B == null || B.length() == 0) {
        return 0;
    }
    int[][] lcs = new int[A.length()][B.length()];
    for (int i = 0; i < A.length(); i++) {
        for (int j = 0; j < B.length(); j++) {
            if (i == 0 || j == 0) {
                lcs[i][j] = A.charAt(i) == B.charAt(j) ? 1 : 0;
            } else {
                if (A.charAt(i) == B.charAt(j)) {
                    lcs[i][j] = lcs[i - 1][j - 1] + 1;
                } else {
                    lcs[i][j] = Math.max(lcs[i - 1][j], lcs[i][j - 1]);
                }
            }
        }
    }
    return lcs[A.length() - 1][B.length() - 1];
}

Longest Common Substring

Given two strings, find the longest common substring.

Return the length of it.

Example
Given A = “ABCD”, B = “CBCE”, return 2.

Note
The characters in substring should occur continuously in original string. This is different with subsequence.

Challenge
O(n x m) time and memory.

Solution: Dynamic Programming

public int longestCommonSubstring(String A, String B) {
    int[][] lcs = new int[A.length()][B.length()];
    int max = 0;
    for (int i = 0; i < A.length(); i++) {
        for (int j = 0; j < B.length(); j++) {
            if (A.charAt(i) == B.charAt(j)) {
                if (i == 0 || j == 0) {
                    lcs[i][j] = 1;
                } else {
                    lcs[i][j] = lcs[i - 1][j - 1] + 1;
                }
            }
            if (lcs[i][j] > max) {
                max = lcs[i][j];
            }
        }
    }
    return max;
}

Distinct Subsequences

Given a string S and a string T, count the number of distinct subsequences of T in S.

A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, "ACE" is a subsequence of "ABCDE"while "AEC" is not).

Example

Given S = "rabbbit", T = "rabbit", return 3.

Challenge

Do it in O(n2) time and O(n) memory.

O(n2) memory is also acceptable if you do not know how to optimize memory.

Solution: Dynamic Programming

Version 1. O(m*n)time O(m*n)space
f[i][j] means the number of different ways to select first j chars of T from first i chars from S, 即从S的前i个字符中挑出T的前j个字符 有多少种方案
f[i][j] = f[i-1][j-1] + f[i-1][j] //S[i]=T[j]
= f[i-1][j] //S[i]!=T[j]

public int numDistinct(String S, String T) {
    //f[i][j] means the number of different ways to select first j chars of T from first i chars from S
    //从S的前i个字符中挑出T的前j个字符 有多少种方案
    //f[i][j] = f[i-1][j-1] + f[i-1][j] //S[i]=T[j]
    //      = f[i-1][j] //S[i]!=T[j]
    if (S == null || T == null || S.length() < T.length()) {
        return 0;
    }
    int[][] numDistinct = new int[S.length() + 1][T.length() + 1];
    for (int i = 0; i <= S.length(); i++) {
        numDistinct[i][0] = 1;
    }
    for (int i = 1; i <= S.length(); i++) {
        for (int j = 1; j <= i && j <= T.length(); j++) {
            if (S.charAt(i - 1) == T.charAt(j - 1)) {
                numDistinct[i][j] = numDistinct[i - 1][j - 1] + numDistinct[i - 1][j];
            } else {
                numDistinct[i][j] = numDistinct[i - 1][j];
            }
        }
    }
    return numDistinct[S.length()][T.length()];
}

Version 2. O(m*n) time, O(n)space

just mod 2 for the row index. Rolling array.

public int numDistinct(String S, String T) {
    if (S == null || T == null || S.length() < T.length()) {
        return 0;
    }
    int[][] numDistinct = new int[2][T.length() + 1];
    for (int i = 0; i < 2; i++) {
        numDistinct[i][0] = 1;
    }
    for (int i = 1; i <= S.length(); i++) {
        for (int j = 1; j <= i && j <= T.length(); j++) {
            if (S.charAt(i - 1) == T.charAt(j - 1)) {
                numDistinct[i % 2][j] = numDistinct[(i - 1) % 2][j - 1] + numDistinct[(i - 1) % 2][j];
            } else {
                numDistinct[i % 2][j] = numDistinct[(i - 1) % 2][j];
            }
        }
    }
    return numDistinct[S.length() % 2][T.length()];
}

Edit Distance

Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2. (each operation is counted as 1 step.)

You have the following 3 operations permitted on a word:

  • Insert a character
  • Delete a character
  • Replace a character
Example

Given word1 = "mart" and word2 = "karma", return 3.

Solution: Dynamic Programming

f[i][j] = MIN(f[i-1][j-1], f[i-1][j]+1, f[i][j-1]+1) // a[i] == b[j]

= MIN(f[i-1][j], f[i][j-1], f[i-1][j-1]) + 1 // a[i] != b[j]

intialize: f[i][0] = i, f[0][j] = j

answer: f[a.length()][b.length()]

public int minDistance(String word1, String word2) {
    if (word1 == null || word2 == null) {
        return -1;
    }
    //minDistance[i][j] = minimum distance to convert first i chars of word 1
    //to first j chars of word 2
    int[][] minDistance = new int[word1.length() + 1][word2.length() + 1];
    //init the first column -- word2 is empty
    for (int i = 0; i < minDistance.length; i++) {
        minDistance[i][0] = i;
    }
    //init the first row -- word1 is empty
    for (int j = 0; j < minDistance[0].length; j++) {
        minDistance[0][j] = j;
    }

    for (int i = 1; i <= word1.length(); i++) {
        for (int j = 1; j <= word2.length(); j++) {
            if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
                minDistance[i][j] = Math.min(Math.min(minDistance[i - 1][j - 1],
                                minDistance[i - 1][j] + 1), minDistance[i][j - 1] + 1);
            } else {
                minDistance[i][j] = Math.min(Math.min(minDistance[i - 1][j - 1],
                                minDistance[i - 1][j]), minDistance[i][j - 1]) + 1;
            }
        }
    }
    return minDistance[word1.length()][word2.length()];
}

Word Break

Given a string s and a dictionary of words dict, determine if s can be break into a space-separated sequence of one or more dictionary words.

Example

Given s = "lintcode", dict = ["lint", "code"].

Return true because “lintcode” can be break as "lint code".

Version 1. DP 常规做法 无优化 straight forward

    public boolean wordSegmentation(String s, Set < String > dict) {
    	if (s == null || dict == null) {
    		return false;
    	}
    	//result[i] means if s.subString(0,i+1) can be break.
    	boolean[] result = new boolean[s.length() + 1];
    	result[0] = true;
    	for (int i = 0; i < s.length(); i++) {
    		if (dict.contains(s.substring(0, i + 1))) {
    			result[i + 1] = true;
    		} else {
    			for (int j = 0; j < i; j++) {
    				if (result[j + 1] && dict.contains(s.substring(j + 1, i + 1))) {
    					result[i + 1] = true;
    					break;
    				}
    			}
    		}
    	}
    	return result[s.length()];
    }

Version 2. DP 优化 根据字典里词的长度 减少查询次数

Jump Game

1. Jump Game(问能不能跳到)

Given an array of non-negative integers, you are initially positioned at the first index of the array.
Each element in the array represents your maximum jump length at that position.

Determine if you are able to reach the last index.

Example

A = [2,3,1,1,4], return true.

A = [3,2,1,0,4], return false.

Solution: Dynamic Programming.

f[i] = if it is possible to jump to i from the beginning.

f[i] = OR(f[j], j < i && j能够跳到i)

 

  public boolean canJump(int[] A) {
    if(A==null|| A.length==0){
      return false;
    }
    boolean[] resultMap = new boolean[A.length];
    resultMap[0] = true;
    for(int i=1;i<A.length;i++){
      for(int j=0;j<i;j++){
        if(resultMap[j] && (i-j)<=A[j]){
          resultMap[i] = true;
          break;
        }
      }
    }
    return resultMap[A.length-1];
  }

2. Jump Game II(问最小step数)

Given an array of non-negative integers, you are initially positioned at the first index of the array.

Each element in the array represents your maximum jump length at that position.

Your goal is to reach the last index in the minimum number of jumps.

Example
Given array A = [2,3,1,1,4]

The minimum number of jumps to reach the last index is 2. (Jump 1 step from index 0 to 1, then 3 steps to the last index.)

Version 1. DP做法 不是最优 这道题可以用greedy

  public int jump(int[] A) {
    if(A==null||A.length==0){
      return 0;
    }
    //minimum number of jumps from start to i
    int[] resultMap = new int[A.length];
    resultMap[0] = 0;
    for(int i=1;i<A.length;i++){
      resultMap[i] = -1;
      for(int j=0;j<i;j++){
        if(resultMap[j]>=0 && (i-j)<=A[j]){
            resultMap[i] = resultMap[j]+1;
            break; //这里包含Greedy思想,最先出现的一定是最优的
        }
      }
    }
    return resultMap[A.length-1];
  }

Version 2. Greedy (O(n) One pass)

二指针问题,最大覆盖区间。从左往右扫描,维护一个覆盖区间,每扫过一个元素,就重新计算覆盖区间的边界。比如,开始时区间[start, end], 遍历A数组的过程中,不断计算A[i]+i最大值(即从i坐标开始最大的覆盖坐标),并设置这个最大覆盖坐标为新的end边界。而新的start边界则为原end+1。不断循环,直到end> n.

public int jump(int[] A) {
    if (A == null || A.length == 0) {
        return 0;
    }
    int start = 0, end = 0, jump = 0;
    while (end < A.length) {
        jump++;
        int furthest = end;
        for (int i = start; i <= end; i++) {
            if (A[i] + i > furthest) {
                furthest = A[i] + i;
            }
        }
        start = end + 1;
        end = furthest;
        if (start > end) {
            return -1; //if can not jump to the end
        }
    }
    return jump;
}