IT技术小屋

秋风秋雨,皆入我心

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  38 随笔 :: 1 文章 :: 19 评论 :: 0 Trackbacks

#

Distinct Subsequences

Given a string S and a string T, count the number of distinct sub sequences 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).
Here is an example:
S = "rabbbit", T = "rabbit"
Return 3.

这两个题目很相似,状态方程是f(i, j) = f(i - 1, j) + S[i] == T[j]? f(i - 1, j - 1) : 0 Where f(i, j) is the number of distinct sub-sequence for T[0:j] in S[0:i].
数组初始情况是第一列全部是1,代表T字符串是空串,S和自己做匹配。实现代码如下:
 1 public class DistinctSubsequences {
 2     public int numDistinct(String S, String T) {
 3         int[][] f = new int[S.length() + 1][T.length() + 1];
 4         for (int k = 0; k < S.length(); k++)
 5             f[k][0] = 1;
 6         for (int i = 1; i <= S.length(); i++) {
 7             for (int j = 1; j <= T.length(); j++) {
 8                 if (S.charAt(i - 1) == T.charAt(j - 1)) {
 9                     f[i][j] += f[i - 1][j] + f[i - 1][j - 1];
10                 } else {
11                     f[i][j] += f[i - 1][j];
12                 }
13             }
14         }
15         return f[S.length()][T.length()];
16     }
17 }

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:
a) Insert a character
b) Delete a character
c) Replace a character

 1 public class EditDistance {
 2     public int minDistance(String word1, String word2) {
 3         if (word1.length() == 0 || word2.length() == 0)
 4             return word1.length() == 0 ? word2.length() : word1.length();
 5         int[][] arr = new int[word2.length() + 1][word1.length() + 1];
 6         for (int i = 0; i <= word1.length(); i++) {
 7             arr[0][i] = i;
 8         }
 9         for (int j = 0; j <= word2.length(); j++) {
10             arr[j][0] = j;
11         }
12         for (int i = 0; i < word1.length(); i++) {
13             for (int j = 0; j < word2.length(); j++) {
14                 if (word1.charAt(i) == word2.charAt(j)) {
15                     arr[j + 1][i + 1] = arr[j][i];
16                 } else {
17                     int dis = Math.min(arr[j][i + 1], arr[j + 1][i]);
18                     arr[j + 1][i + 1] = Math.min(arr[j][i], dis) + 1;
19                 }
20             }
21         }
22         return arr[word2.length()][word1.length()];
23     }
24 }
posted @ 2013-12-31 10:21 Meng Lee 阅读(232) | 评论 (0)编辑 收藏

给定一个无序的整数数组,怎么找到第一个大于0,并且不在此数组的整数。比如[1,2,0] 返回 3, [3,4,-1,1] 返回 2。最好能O(1)空间和O(n)时间。
 1 public class Solution {
 2     public int findMissedNumber(int[] nums) {
 3         for (int i = 0; i < nums.length; i++) {
 4             if (nums[i] > 0 && nums[i] - 1 != i && nums[i] != nums[nums[i] - 1]) {
 5                 int tmp = nums[nums[i] - 1];
 6                 nums[nums[i] - 1] = nums[i];
 7                 nums[i] = tmp;
 8                 i--;
 9             }
10         }
11         for (int j = 0; j < nums.length; j++) {
12             if (nums[j] - 1 != j) return j + 1;
13         }
14         return nums.length + 1;
15     }
16 }
posted @ 2013-12-28 15:31 Meng Lee 阅读(133) | 评论 (0)编辑 收藏

3个字符串a,b,c。判断c是否是a和b的interleave,也就是c中应该有a,b中所有字 符,并且c中字符顺序和a,b中一样。比如,
1. a = "ef" b = "gh" c = "egfh" return true
2. a = "ef" b = "gh" c = "ehgf" return false

分析:
这个题目中,并没有说明a和b中是否有相同的字符,这个直接影响了最终的解法。所以,大家在面试的过程中,要和面试官进行交互,弄清楚之后再动手。a和b中不含有相同字符的情况很简单,这里略去。下面给出a和b中包含相同字符的动态规划的解法。

 1 public class Solution {
 2     public boolean isInterleaved(String a, String b, String c) {
 3         int lengthA = a.length();
 4         int lengthB = b.length();
 5         int lengthC = c.length();
 6         if (lengthA + lengthB != lengthC)
 7             return false;
 8         boolean[][] map = new boolean[lengthB + 1][lengthA + 1];
 9         map[0][0] = true;
10         for (int m = 1; m < lengthA; m++) {
11             map[0][m] = (a.charAt(m - 1) == c.charAt(m - 1) && map[0][m - 1]);
12         }
13         for (int n = 1; n < lengthB; n++) {
14             map[n][0] = (b.charAt(n - 1) == c.charAt(n - 1) && map[n - 1][0]);
15         }
16         for (int i = 1; i <= lengthB; i++) {
17             for (int j = 1; j <= lengthA; j++) {
18                 map[i][j] = (c.charAt(i + j - 1) == b.charAt(i - 1) && map[i - 1][j])
19                         || (c.charAt(i + j - 1) == a.charAt(j - 1) && map[i][j - 1]);
20             }
21         }
22         return map[lengthB][lengthA];
23     }
24 }


posted @ 2013-12-28 14:29 Meng Lee 阅读(147) | 评论 (0)编辑 收藏

删除字符串中的“b”和“ac”,需要满足如下的条件:
1. 字符串只能遍历一次
2. 不能够实用额外的空间

例如:
1. acbac   ==>  ""
2. aaac    ==>  aa
3. ababac  ==>   aa
4. bbbbd   ==>   d
5. aaccac  ==> ""

 1 public class Solution {
 2     public String deleteChars(String s) {
 3         StringBuffer sb = new StringBuffer(s);
 4         int fast = 0, slow = -1;
 5         int length = s.length();
 6         while (fast < length) {
 7             if (sb.charAt(fast) == 'b') {
 8                 fast++;
 9             } else if (fast < length - 1 && sb.charAt(fast) == 'a' && sb.charAt(fast + 1) == 'c') {
10                 fast += 2;
11             } else {
12                 sb.setCharAt(++slow, sb.charAt(fast++));
13                 if (slow > 0 && sb.charAt(slow - 1) == 'a' && sb.charAt(slow) == 'c') {
14                     slow -= 2;
15                 }
16             }
17         }
18         return sb.substring(0, slow + 1);
19     }
20 }


posted @ 2013-12-28 11:02 Meng Lee 阅读(102) | 评论 (0)编辑 收藏

对一个字符串按照回文进行分割,例如aba|b|bbabb|a|b|aba就是字符串ababbbabbababa的一个回文分割,每一个字串都是一个回文。请找到可以分割的最少的字串数。例如:
1. ababbbabbababa最少4个字符串,分割三次:a|babbbab|b|ababa
2. 如果字符串整体是回文,则需要0次分割,最少1个字符串

分析:
递归方程为:f(i) = MIN(f(j - 1) + 1), map[j][i] == true, 0<=j<i,其中f(i)为以第i个字符结尾的字符串的最小切割次数,map数组用来记录s[i][j]是否是对称的。实现代码如下:
 1 public class Solution {
 2     public int minPalindromePartition(String s) {
 3         int length = s.length();
 4         boolean[][] map = new boolean[length][length];
 5         int[] record = new int[length];
 6         for (int k = 0; k < length; k++) {
 7             record[k] = k;
 8         }
 9         for (int offset = 0; offset < length; offset++) {
10             for (int i = 0, j = i + offset; i < length - offset; i++, j++) {
11                 if (i == j || (s.charAt(i) == s.charAt(j) && (j - i == 1 || map[i + 1][j - 1]))) {
12                     map[i][j] = true;
13                 }
14             }
15         }
16         for (int i = 1; i < length; i++) {
17             for (int j = 0; j < i; j++) {
18                 if (map[j][i]) {
19                     if (j > 0) {
20                         record[i] = Math.min(record[i], record[j - 1] + 1);
21                     } else {
22                         record[i] = 0;
23                     }
24                 }
25             }
26         }
27         return record[length - 1];
28     }
29 }
posted @ 2013-12-27 16:06 Meng Lee 阅读(127) | 评论 (0)编辑 收藏

求二叉搜索树的中序后继节点

分析:
1. 如果目标节点的右子树不为空,则返回右子树的最小节点;
2. 如果目标节点的右子树为空,则从根节点开始遍历。
实现代码如下:
 1 public class Solution {
 2     public TreeNode inOrderSuccessor(TreeNode root, TreeNode target) {
 3         if (target == nullreturn null;
 4         if (target.right != nullreturn minValue(target.right);
 5         TreeNode succ = null;
 6         while (root != null) {
 7             if (target.val < root.val) {
 8                 succ = root;
 9                 root = root.left;
10             } else if (target.val > root.val) {
11                 root = root.right;
12             } else {
13                 break;
14             }
15         }
16         return succ;
17     }
18 
19     private TreeNode minValue(TreeNode root) {
20         while (root.left != null) {
21             root = root.left;
22         }
23         return root;
24     }
25 }
posted @ 2013-12-27 11:06 Meng Lee 阅读(197) | 评论 (0)编辑 收藏

最少插入字符

给定字符串,可以通过插入字符,使其变为回文。求最少插入字符的数量。例如:
1. ab最少插入1个字符,变为bab
2. aa最少插入0个字符
3. abcd最少插入3个字符,dcbabcd

分析
    这个题目的分析思路,和前面两期是非常相似的:给出递归的解法,发现重复的子问题,改进为动态规划的解法,这是一个分析的过程,待同学们比较熟悉时候,可以直接给出动态规划的解决方案,就很好了。
    这个题目,递归该如何解呢?给定一个字符串str,长度为n,怎么插入最少的字符,是的字符串变为回文呢?插入最少的字符,就是要尽量利用原来的字符,在原字符串str中,尽量利用更多能够匹配的字符。怎么对这个问题进行分解呢?考虑str字符串整体:
    1. 如果str[0]==str[n-1],则问题转变为求str[1,n-2],插入最少字符,得到回文
    2. 如果str[0]!=str[n-1],则需要插入一个字符要么和str[0]相同,要么和str[n-1]相同,
    3. 如果和str[0],则转变为str[1,n-1],插入最少字符,得到回文
    4. 如果和str[n-1],则转变为str[0,n-2],插入最少字符,得到回文
    上面的第2种情况中,需要取两个值最小值。则完成了问题的分解,并且,基本情况也分析完全,则有递归式为:
    fmi(str, l, h) = (str[l] == str[h]) ? fmi(str, l+1, h-1) : (min(fmi(str, i+1, h), fmi(str,l, h-1))+1)
    通过上面的式子,有经验的、熟练的同学,很直接的就能看出来,存在重复的子问题,这就意味着,我们可以讲子问题的解缓存使用。如果,没有直接能够看出来的同学们,还是可以按照我们之前的方法,把递归树画出来吧,那样更加一目了然。
    那么,这个题目该如何用动态规划的解决呢?如何重复利用子问题的解呢?似乎有些不那么直接。但其实也是于规律可循的。上面的递归式,是从字符串的两 边,想中间移动递归,根据动态规划解决问题的思想,我们先解决子问题,再重复利用子问题,就是要从内向外解决,大家还记得回文子串判断的那个题目么,动态 规划解法的外层循环是子串的长度,这个题目也是类似的。示例代码如下:
 1 public class Solution {
 2     public int constructPalindrome(String s) {
 3         int length = s.length();
 4         int[][] map = new int[length][length];
 5         for (int offset = 1; offset < length; offset++) {
 6             for (int i = 0, j = offset; i < length - offset; i++, j++) {
 7                 if (i == j - 1) {
 8                     if (s.charAt(i) != s.charAt(j)) {
 9                         map[i][j] = 1;
10                     }
11                 } else {
12                     if (s.charAt(i) != s.charAt(j)) {
13                         map[i][j] = Math.min(map[i][j - 1], map[i + 1][j]) + 1;
14                     } else {
15                         map[i][j] = map[i + 1][j - 1];
16                     }
17                 }
18             }
19         }
20         return map[0][length - 1];
21     }
22 }
posted @ 2013-12-26 16:53 Meng Lee 阅读(158) | 评论 (0)编辑 收藏

1、给定一组区间,表示为[start,end],请给出方法,将有重叠的区间进行合并。例如:
给定:[1,3],[2,6],[8,10],[15,18],
合并:[1,6],[8,10],[15,18].
分析:题目很直观,首先把区间递增排序,然后从头合并,合并时观察当前区间的start是否小于或等于前一个区间的end。代码如下:
 1 public class MergeIntervals {
 2     public class IntervalCmp implements Comparator<Interval> {
 3         @Override
 4         public int compare(Interval i1, Interval i2) {
 5             if (i1.start < i2.start) return -1;
 6             if (i1.start == i2.start && i1.end <= i2.end) return -1;
 7             return 1;
 8         }
 9     }
10     
11     public ArrayList<Interval> merge(ArrayList<Interval> intervals) {
12         ArrayList<Interval> ret = new ArrayList<Interval>();
13         if (intervals.size() == 0) return ret;
14         Interval[] arr = new Interval[intervals.size()];
15         intervals.toArray(arr);
16         Arrays.sort(arr, new IntervalCmp());
17         int start = arr[0].start;
18         int end = arr[0].end;
19         for (int i = 0; i < arr.length; i++) {
20             if (arr[i].start <= end) {
21                 end = Math.max(end, arr[i].end);
22             } else {
23                 ret.add(new Interval(start, end));
24                 start = arr[i].start;
25                 end = arr[i].end;
26             }
27         }
28         ret.add(new Interval(start, end));
29         return ret;
30     }
31 }
2、给定的一组区间,将区间中的存在的任意区间的父区间删除,例如:
给定:[1,2] [1,3] [1,4] [5,9] [6,8]
删除后:[1,2] [6,8]
分析:此题暴力的解法的复杂度为O(N^2)。受上一题排序的启发,是否有好一点的思路呢?
我们可以按照start递增排序,若start相等,则按照end递减排序。考虑排序后的第i-1 和第i个区间,由于start是递增的,所以第i-1个区间的start肯定小于等于第i个区间的start。若第i-1个区间的end大于等于第i个区间的end,则第i-1个区间就成为第i个区间的父区间了。

按照这个思路,可以试着在排序之后逆向顺序处理每一个区间。假设当前处理第i个区间,如前所述,若第i-1个区间的end大于等于第i个区间的end,则第i-1个区间成为第i个区间的父区间,可以保留第i个区间,将第i-1个区间删除。由于第i-1个区间是第i个区间的父区间,所以第i-1个区间的父区间也是第i个区间的父区间,这种情形下删掉第i-1个区间,后续不会漏删第i-1个区间的父区间。若第i-1个区间的end小于第i个区间的end,则可以跳过第i个区间,开始处理第i-1个区间。因为按照这种处理的方法,在处理到第i个区间时,该区间不会是任何区间的父区间(若是父区间已经被如前所述的方法删除了)。而且,在这种情形下,后续可能出现的第i个区间的父区间会是第i-1个区间的父区间,所以也不会漏掉第i个区间的父区间。所以排序之后逆向处理,只需要O(N)的时间,就可以解决这道问题。整体的时间复杂度为O(nlogn)。
 1 public class Solution {
 2     public class IntervalCmp implements Comparator<Interval> {
 3         @Override
 4         public int compare(Interval i1, Interval i2) {
 5             if (i1.start < i2.start) return -1;
 6             if (i1.start == i2.start && i1.end >= i2.end) return -1;
 7             return 1;
 8         }
 9     }
10     
11     public ArrayList<Interval> merge(ArrayList<Interval> intervals) {
12         ArrayList<Interval> ret = new ArrayList<Interval>();
13         if (intervals.size() == 0) return ret;
14         Interval[] arr = new Interval[intervals.size()];
15         intervals.toArray(arr);
16         Arrays.sort(arr, new IntervalCmp());
17         int start = arr[arr.length - 1].start;
18         int end = arr[arr.length - 1].end;
19         for (int i = arr.length - 2; i >= 0; i--) {
20             if (arr[i].end < end) {
21                 ret.add(new Interval(start, end));
22                 start = arr[i].start;
23                 end = arr[i].end;
24             }
25         }
26         ret.add(new Interval(start, end));
27         return ret;
28     }
29 }
posted @ 2013-12-26 14:47 Meng Lee 阅读(1785) | 评论 (0)编辑 收藏

Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.
For example, given the following triangle
[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]
The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11).
Note:
Bonus point if you are able to do this using only O(n) extra space, where n is the total number of rows in the triangle.

本题本来的想法是用递归做,实现代码如下:
 1 public class Solution {
 2     public int minimumTotal(ArrayList<ArrayList<Integer>> triangle) {
 3         int row = triangle.size();
 4         return findMinPath(triangle, 0, 0, row);
 5     }
 6 
 7     private int findMinPath(ArrayList<ArrayList<Integer>> triangle, int row,
 8             int col, int totalRow) {
 9         if (row == totalRow - 1) {
10             return triangle.get(row).get(col);
11         } else {
12             return triangle.get(row).get(col) + Math.min(findMinPath(triangle, row + 1, col, totalRow), findMinPath(triangle, row + 1, col + 1, totalRow));
13         }
14     }
15 }
提交之后发现超时,于是考虑到可能是递归的开销问题,考虑用迭代解题。实现如下:
 1 public class Triangle {
 2     public int minimumTotal(ArrayList<ArrayList<Integer>> triangle) {
 3         int n = triangle.size() - 1;
 4         int[] path = new int[triangle.size()];
 5         for (int o = 0; o < triangle.get(n).size(); o++) {
 6             path[o] = triangle.get(n).get(o);
 7         }
 8         for (int i = triangle.size() - 2; i >= 0; i--) {
 9             for (int j = 0, t = 0; j < triangle.get(i + 1).size() - 1; j++, t++) {
10                 path[t] = triangle.get(i).get(t)
11                         + Math.min(path[j], path[j + 1]);
12             }
13         }
14         return path[0];
15     }
16 }
这个解法的核心是从叶节点自底向上构造解空间。
posted @ 2013-12-25 11:31 Meng Lee 阅读(141) | 评论 (0)编辑 收藏

Subsets的解法是从空集开始,依次取每一个元素与子集中的每个集合做并操作。实现代码如下:

 1 public class Subsets {
 2     public ArrayList<ArrayList<Integer>> subsets(int[] S) {
 3         Arrays.sort(S);
 4         ArrayList<ArrayList<Integer>> subsets = new ArrayList<ArrayList<Integer>>();
 5         subsets.add(new ArrayList<Integer>());
 6         for (int i = 0; i < S.length; i++) {
 7             int size = subsets.size();
 8             for (int j = 0; j < size; j++) {
 9                 ArrayList<Integer> subset = new ArrayList<Integer>(
10                         subsets.get(j));
11                 subset.add(S[i]);
12                 subsets.add(subset);
13             }
14         }
15         return subsets;
16     }
17 }

Gray Code的算法是初始时集合中只有{0, 1}两个元素,此后在每个元素的前面附加一个1。实现代码如下:

 1 public class GrayCode {
 2     public ArrayList<Integer> grayCode(int n) {
 3         ArrayList<Integer> result = new ArrayList<Integer>();
 4         if (n == 0) {
 5             result.add(0);
 6             return result;
 7         }
 8         ;
 9         result.add(0);
10         result.add(1);
11         for (int i = 1; i < n; i++) {
12             ArrayList<Integer> tmp = new ArrayList<Integer>(result);
13             Integer a = 1 << i;
14             for (int k = result.size() - 1; k >= 0; k--) {
15                 tmp.add(result.get(k) + a);
16             }
17             result = tmp;
18         }
19         return result;
20     }
21 }

posted @ 2013-12-24 12:06 Meng Lee 阅读(243) | 评论 (0)编辑 收藏

仅列出标题
共4页: 上一页 1 2 3 4 下一页