IT技术小屋

秋风秋雨,皆入我心

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

2013年12月31日 #

本文介绍了包括 Python、Java、Haskell等在内的一系列编程语言的深度学习库。

Python
  • Theano是一种用于使用数列来定义和评估数学表达的 Python 库。它可以让 Python 中深度学习算法的编写更为简单。很多其他的库是以 Theano 为基础开发的。
  • Caffe是一种以表达清晰、高速和模块化为理念建立起来的深度学习框架。它是由伯克利视觉和学习中心(BVLC)和网上社区贡献者共同开发的。谷歌的 DeepDream 人工智能图像处理程序正是建立在 Caffe 框架之上。这个框架是一个 BSD 许可的带有 Python 接口的 C++库。
  • nolearn包含大量其他神经网络库中的包装器和抽象(wrappers and abstractions),其中最值得注意的是 Lasagne,其中也包含一些机器学习的实用模块。
  • Genism是一个部署在 Python 编程语言中的深度学习工具包,用于通过高效的算法处理大型文本集。
  • Chainer连接深度学习中的算法与实现,它强劲、灵活而敏锐,是一种用于深度学习的灵活的框架。
  • deepnet是一种基于 GPU 的深度学习算法的 Python 实现,比如:前馈神经网络、受限玻尔兹曼机、深度信念网络、自编码器、深度玻尔兹曼机和卷积神经网络。
  • Hebel是一个在 Python 中用于带有神经网络的深度学习的库,它通过 PyCUDA 使用带有 CUDA 的 GPU 加速。它可实现大多数目前最重要的神经网络模型,提供了多种不同的激活函数和训练方式,如动量,Nesterov 动量,退出(dropout)和 前期停止(early stopping)。
  • CXXNET是一种快速,简明的分布式深度学习框架,它以 MShadow 为基础。它是轻量级可扩展的 C++/CUDA 神经网络工具包,同时拥有友好的 Python/Matlab 界面,可供机器学习的训练和预测使用。
  • DeepPy是一种建立在 Mumpy 之上的 Python 化的深度学习框架。
  • DeepLearning是一个用 C++和 Python 开发的深度学习库。
C++
  • eblearn是一个机器学习的开源 C++库,由纽约大学机器学习实验室的 Yann LeCun 牵头研发。尤其是,按照 GUI、演示和教程来部署的带有基于能量的模型的卷积神经网络。
  • SINGA被设计用来进行已有系统中分布式训练算法的普通实现。它由 Apache Software Foundation 提供支持。
Java
  • N-Dimensional Arrays for Java (ND4J)是一种为 JVM 设计的科学计算库。它们被应用在生产环境中,这就意味着路径被设计成可以最小的 RAM 内存需求来快速运行。
  • Deeplearning4j是第一个为 Java 和 Scala 编写的消费级开元分布式深度学习库。它被设计成在商业环境中使用,而非研究工具。
  • Encog是一种先进的机器学习框架,支持支持向量机(Support Vector Machines),人工神经网络(Artificial Neural Networks),基因编程(Genetic Programming),贝叶斯网络(Bayesian Networks),隐马尔科夫模型(Hidden Markov Models)和 遗传算法(Genetic Algorithms)。
Lua
  • Torch是一种科学计算框架,可支持多种计算机学习算法。
Haskell
  • DNNGraph是一个用 Haskell 编写的深度神经网络生成 DSL。
.NET
  • Accord.NET是一种.NET 机器学习框架,包含声音和图像处理库,它完全由 C# 编写。它是一种为开发生产级的计算机视觉、计算机听觉、信号处理和统计应用而设计的完整框架。
R
  • darch包可以用于建立多层神经网络(深层结构)。其中的训练方式包括使用对比发散法进行提前训练,或使用通常的训练方法(如反向传播和共轭梯度)进行一些微调。
  • deepnet实现了一些深度学习架构和神经网络算法,包括 BP、RBM、DBN、深度自编码器等等。

posted @ 2016-11-13 00:45 Meng Lee 阅读(434) | 评论 (0)编辑 收藏

今天,终于有时间静下心来回顾过去两年来所做的事情,感慨万千,一时之间竟不知从何说起。两年以来,遇到的困难着实不少,但每每遭遇挫折与不顺之后,却往往能柳暗花明,遇到新的转机,让我真真切切地感受到了功夫不负有心人这句话的含意。

一、为什么要出国
其实,之前从来没有考虑过要出国,更没有想过能直接出国工作。回想一下,这个决定的做出,多半还是缘于自己骨子里的不安分。我从很大程度上来说是一个闲不住的人,从小学、中学、大学到研究生,我几乎每天都有明确的目标。然而,2013年从公司到事业单位工作以后,我的生活发生了巨大地转变。简单的工作、空洞的公文、无聊的活动占据了我全部的工作任务。有段时间几乎天天写材料搞活动。领导经常夸我材料写得又快又好,活动也搞得有声有色,心里感觉很有成就感。然而,时间一长,逐渐发现那些公文永远是一个套路,以至于我分门别类,摸索出了几个万能模板。而活动则千篇一律,让人疲于应付。我甚至可以看到六十岁退休时我在干什么,于是一阵恐惧感常常会莫名袭来,因为我不安分、不满足于此。我不能放弃所学所长,我不能庸庸碌碌在这里度过未来的几十年,我还有梦想,我还要登高看世界。为了这个,我走过了不平凡的两年。

二、如何出国
对于普通人来说,出国大致有三条路。
第一条路是申请去国外留学,取得学位之后以应届毕业生的身份找工作,然后留在国外生活。这是一条比较稳妥、简便的路,走这条路的人最多。
第二条路是先进入跨国公司的中国分公司工作一段时间,然后找机会外派到国外总部工作。走这条路的要求比较多,首先要能够进入比较大的跨国公司工作,其次这个公司愿意将中国员工transfer到国外,同时还要外国总部有部门愿意接收你,所以还是需要一些运气。但是,如果成功,好处也显而易见。省去了读书的时间和学费,降低了家庭负担,对于家境一般的人是非常好的选择。
第三条路是直接参加外国公司的面试,通过之后直接去国外工作。这条路要求最高,需要通过外国公司严格的面试,另外还要能够成功取得签证(美国工作签证就需要抽签)。因此,走这条路更需要实力、机遇和运气。
鉴于第三条路非常难走,为了保证成功,我选择了同时申请学校和参加外国公司面试的办法,这也注定了我将付出更多的艰苦努力。

三、申请学校
申请学校从准备到最终完成,我大概用了一年时间。其间参加了三次GRE和一次托福考试。回想准备的过程,最大的敌人就是自己,最重要的法宝就是坚持坚持再坚持。记得第一次考GRE没有取得理想的成绩,因为是第一次参加英语考试,心情非常失落。幸亏当时有女朋友(现在的老婆)的鼓励,我继续复习没有放弃。经过一个月的复习,取得了非常不错的托福成绩。记得托福出成绩的那天,我紧张得不敢查,点开页面的那一刻,我都不敢相信居然能有这么不错的成绩。特别是听力,考试的时候觉得好几个都没有听清楚,最后居然有27分,真是不可思议,可见功夫不负有心人,付出总有回报的。
有了英语成绩之后,就是撰写申请文书。这方面我完全没有经验,所有的信息全部是通过一亩三分地论坛获得的。这个论坛信息非常丰富,基本上所有申请相关的内容都有涉及。我每天都会花一些时间浏览别人的帖子,为自己定位选校,找文书灵感等等。非常感谢我的本科和研究生导师,还有蒋志诚为我递推荐信,没有你们的帮助,我不可能完成申请工作。
最后,我申请了美国和加拿大的十五所学校的计算机专业的研究生,拿到了CMU、USC和多伦多大学的offer。其中,CMU的Data Science program应该是世界数一数二的,录取率非常低,毕业后的去向也非常好,大多数都可以进入美国一流公司工作。多大也是加拿大排名第一的学校,计算机的就业也非常好。

四、Facebook的面试
参加Facebook的面试也完全是无意的,在Linkedin上收到了Facebook HR的邀请信,于是也没有怎么准备就做了电面,居然反馈非常好,马上就给我安排了onsite面试,地点是印度的海得拉巴。但是,始终是没有做什么准备,而且和谷歌不一样的是,HR办事效率实在太高,每一轮间隔都非常短,导致我根本没有时间热身一下,连leetcode都没有做过就匆匆参加面试了,最终没有如愿通过面试。
不过,这次面试还是很有收获。第一次出国,第一次参加美国公司全英文面试,学到了太多,积累了经验,可以说如果没有Facebook的失败,我是不可能进入谷歌的。

五、Google的面试
参加谷歌的面试可以说完全是老婆的怂恿。从印度参加完Facebook面试回来之后,我就开始专心于学校申请了。但是,老婆建议我试试面一下Google。由于Facebook的失利和Google近乎苛刻的面试流程,我开始是抗拒参加的。最后,在老婆的一再要求下,我终于找了一个在谷歌上海工作的师兄做了内推。四月底我收到了谷歌北京HR的第一通电话,也正式拉开了我为期一年的面试流程。
和HR通电话不久,我进行了第一次电话面试。谷歌的电话面试和Facebook差不多,就是面试官打过来,把题目口述并且写在Google Doc上,然后我把程序写在Google Doc上。第一次电面的题目不难,但谷歌对代码效率和清晰度的要求远远超出了我的想像。第一轮面得磕磕绊绊,但是幸好面试官是中国人,非常nice,没有让我fail。
于是,我又被要求进行第二次电面。期间由于面试官临时有事爽约,我等了差不多一个月。但是,也就是这一个月,我努力做了一些准备,虽然面试依旧不是十全十美,但是我还是有惊无险地进入到了onsite面试环节。
虽然可以onsite面试了,但是我依旧对进入谷歌不报任何希望,因为我清楚的知道,谷歌面试实在是太难了,onsite面试的挑战将远远大于电面。因此,我去北京面试完全是想做一次免费旅游。面试前一天还许久不见的万威夫妇吃饭,聊得很开心,完全没有把面试放在心上。
也许是放松的原因,我前一天晚上睡得很好,第二天我精神非常好。
不过谷歌毕竟是谷歌,面试第一轮一上来就给了我一个下马威。一个coding题一个设计题,表面上很简单,但是做出来总是有这样那样的问题,第一轮完了之后我基本打算回家了。
但是,不知道为什么,从第二轮开始,就越来越顺利,coding做得非常好,基本上是一次写完,没有涂改,也没有被面试官找到大的bug。突然之间,隐隐感觉出现了一丝希望。
四轮过后,我结束了第一次onsite面试。但是,三天之后,我被告知由于设计题做得不好,我被要求进行一次加面,地点在上海。于是,我又在上海做了一次面试,只有一个设计题。我感觉答得还可以,但是心情真的是忐忑不安,特别是接下来的一个礼拜,几乎是坐立不安。
记得是一个礼拜之后的礼拜五中午,我正做准备主持下午的道德讲堂,突然接到了一个010的电话,我知道是谷歌的电话。接通电话的那一刻,空气都几乎要凝固了,当听到通过HC的消息时,我激动得不能自已。不可能完成的任务居然完成了,虽然不知道能不能去美国总部工作,但是能进入谷歌已经非常不容易了,而且谷歌非常鼓励transfer去美国工作,因此机会还是很多的。
然而,让我没有想到的是,接下来的team match却异常艰难,陆陆续续几个team都没有成功match上。转眼就到了2014年春季,半年的等待让我对何时进入谷歌非常悲观,加上申请学校工作十分繁重,我基本没有关注这个事情。
就在我快要放弃的时候,拿到了美国一个公司的offer,他们答应给我办H1B签证。于是,我把这个情况告诉了谷歌,要求他们尽快给找到team,不然我就去美国了。结果谷歌居然在三天之内为我match上了英国office的一个team,让人不得不感叹还是要offer多才好啊!于是,我又经过了近三个月的签证办理流程,终于要启程赴英了。

回顾两年来的努力,终于要实现自己的梦想了,感慨万千。在短短的人生中,能有这一段不寻常的经历,我觉得十分幸运。展望未来,我想读万卷书不如行万里路,未来希望能够利用在伦敦工作的机会,尽量多去欧洲各国走走,丰富自己的阅历,开拓自己的眼界。

最后要感谢老婆一直以来的支持和鼓励,你一直是我前进的动力;其次要感谢父母的不理解和不支持,你们的反对让我更加完善了自己的计划,逼着我找到了一条最好的出路;还要感谢师长和朋友们的帮助,感谢杨老师和沈老师还有蒋志诚不厌其烦地帮我递推荐信,感谢万威夫妇多次在北京款待我,没有你们的美食,我是不可能完成面试的;还有许多帮助过我的人,在这里就不能一一感谢了。
posted @ 2014-06-13 02:00 Meng Lee 阅读(1455) | 评论 (0)编辑 收藏

算法很简单,核心思想是:对某个值A[i]来说,能trapped的最多的water取决于在i之前最高的值leftMostHeight[i]和在i右边的最高的值rightMostHeight[i]。(均不包含自身)。如果min(left,right) > A[i],那么在i这个位置上能trapped的water就是min(left,right) – A[i]。
有了这个想法就好办了,第一遍从左到右计算数组leftMostHeight,第二遍从右到左计算rightMostHeight,在第二遍的同时就可以计算出i位置的结果了,而且并不需要开空间来存放rightMostHeight数组。
时间复杂度是O(n),只扫了两遍。

 1 public class TrappingRainWater {
 2     public int trap(int A[], int n) {
 3         if (n <= 2)
 4             return 0;
 5 
 6         int[] lmh = new int[n];
 7         lmh[0] = 0;
 8         int maxh = A[0];
 9         for (int i = 1; i < n; ++i) {
10             lmh[i] = maxh;
11             if (maxh < A[i])
12                 maxh = A[i];
13         }
14         int trapped = 0;
15         maxh = A[n - 1];
16         for (int i = n - 2; i > 0; --i) {
17             int left = lmh[i];
18             int right = maxh;
19             int container = Math.min(left, right);
20             if (container > A[i]) {
21                 trapped += container - A[i];
22             }
23             if (maxh < A[i])
24                 maxh = A[i];
25         }
26         return trapped;
27     }
28 }
posted @ 2014-01-14 09:16 Meng Lee 阅读(201) | 评论 (0)编辑 收藏

The set [1,2,3,…,n] contains a total of n! unique permutations.
By listing and labeling all of the permutations in order,
We get the following sequence (ie, for n = 3):
"123"
"132"
"213"
"231"
"312"
"321"
Given n and k, return the kth permutation sequence.
Note: Given n will be between 1 and 9 inclusive.

这道题其实有很强的规律可循。首先,n个元素的排列总数是n!。在下面的分析中,让k的范围是0 <= k < n!。(题目代码实际上是1<=k<=n!)
可以看到一个规律,就是这n!个排列中,第一位的元素总是(n-1)!一组出现的,也就说如果p = k / (n-1)!,那么排列的最开始一个元素一定是arr[p]。
这个规律可以类推下去,在剩余的n-1个元素中逐渐挑选出第二个,第三个,...,到第n个元素。程序就结束。
 1 /**
 2  * The set [1,2,3,…,n] contains a total of n! unique permutations.
 3  * 
 4  * By listing and labeling all of the permutations in order, We get the
 5  * following sequence (ie, for n = 3):
 6  * 
 7  * "123" "132" "213" "231" "312" "321" Given n and k, return the kth permutation
 8  * sequence.
 9  * 
10  * Note: Given n will be between 1 and 9 inclusive.
11  * 
12  */
13 
14 public class PermutationSequence {
15     public String getPermutation(int n, int k) {
16         char[] arr = new char[n];
17         int pro = 1;
18         for (int i = 0; i < n; ++i) {
19             arr[i] = (char) ('1' + i);
20             pro *= (i + 1);
21         }
22         k = k - 1;
23         k %= pro;
24         pro /= n;
25         for (int i = 0; i < n - 1; ++i) {
26             int selectI = k / pro;
27             k = k % pro;
28             pro /= (n - i - 1);
29             int temp = arr[selectI + i];
30             for (int j = selectI; j > 0; --j) {
31                 arr[i + j] = arr[i + j - 1];
32             }
33             arr[i] = (char) temp;
34         }
35         return String.valueOf(arr);
36     }
37 }
38 
posted @ 2014-01-07 16:06 Meng Lee 阅读(425) | 评论 (0)编辑 收藏

Lowest Common Ancestor of a Binary Search Tree (BST)
Given a binary search tree (BST), find the lowest common ancestor of two given nodes in the BST.
       _______6______
      /              \
   ___2__          ___8__
  /      \        /      \
  0      _4       7       9
        /  \
        3   5
Using the above tree as an example, the lowest common ancestor (LCA) of nodes 2 and 8 is 6. 
But how about LCA of nodes 2 and 4? Should it be 6 or 2?
According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between 
two nodes v and w as the lowest node in T that has both v and w as descendants (where we allow a 
node to be a descendant of itself).” Since a node can be a descendant of itself, the LCA of 2 and 
4 should be 2, according to this definition.
Hint:
A top-down walk from the root of the tree is enough.

 1 public class LowestCommonAncestorOfaBinarySearchTree {
 2     public TreeNode LCA(TreeNode root, TreeNode p, TreeNode q) {
 3         if (root == null || p == null || q == null)
 4             return null;
 5         if (Math.max(p.val, q.val) < root.val)
 6             return LCA(root.left, p, q);
 7         if (Math.min(p.val, q.val) > root.val)
 8             return LCA(root.right, p, q);
 9         return root;
10     }
11 }


Given a binary tree, find the lowest common ancestor of two given nodes in the tree.
 
 
        _______3______
       /              \
    ___5__          ___1__
   /      \        /      \
  6      _2       0       8
         /  \
         7   4
If you are not so sure about the definition of lowest common ancestor (LCA), please refer to my previous 
post: Lowest Common Ancestor of a Binary Search Tree (BST) or the definition of LCA here. Using the tree 
above as an example, the LCA of nodes 5 and 1 is 3. Please note that LCA for nodes 5 and 4 is 5.
 
Hint:
Top-down or bottom-up? Consider both approaches and see which one is more efficient.
 1 public class LowestCommonAncestorOfBinaryTree {
 2     public TreeNode LCA(TreeNode root, TreeNode p, TreeNode q) {
 3         if (root == null)
 4             return null;
 5         if (root == p || root == q)
 6             return root;
 7         TreeNode left = LCA(root.left, p, q);
 8         TreeNode right = LCA(root.right, p, q);
 9         if (left != null && right != null)
10             return root;
11         return left != null ? left : right;
12     }
13 }
posted @ 2014-01-06 09:32 Meng Lee 阅读(323) | 评论 (0)编辑 收藏

Given a string s1, we may represent it as a binary tree by partitioning it to two non-empty substrings recursively.
Below is one possible representation of s1 = "great":
    great
   /    \
  gr    eat
 / \    /  \
g   r  e   at
           / \
          a   t
To scramble the string, we may choose any non-leaf node and swap its two children.
For example, if we choose the node "gr" and swap its two children, it produces a scrambled string "rgeat".
    rgeat
   /    \
  rg    eat
 / \    /  \
r   g  e   at
           / \
          a   t
We say that "rgeat" is a scrambled string of "great".
Similarly, if we continue to swap the children of nodes "eat" and "at", it produces a scrambled string "rgtae".
    rgtae
   /    \
  rg    tae
 / \    /  \
r   g  ta  e
       / \
      t   a
We say that "rgtae" is a scrambled string of "great".
Given two strings s1 and s2 of the same length, determine if s2 is a scrambled string of s1.

 1 public class ScrambleString {
 2     public boolean isScramble(String s1, String s2) {
 3         if (s1.length() != s2.length())
 4             return false;
 5         if (s1.equals(s2))
 6             return true;
 7 
 8         int[] A = new int[26];
 9         for (int i = 0; i < s1.length(); i++) {
10             ++A[s1.charAt(i) - 'a'];
11         }
12 
13         for (int j = 0; j < s2.length(); j++) {
14             --A[s2.charAt(j) - 'a'];
15         }
16 
17         for (int k = 0; k < 26; k++) {
18             if (A[k] != 0)
19                 return false;
20         }
21 
22         for (int i = 1; i < s1.length(); i++) {
23             boolean result = isScramble(s1.substring(0, i), s2.substring(0, i))
24                     && isScramble(s1.substring(i), s2.substring(i));
25             result = result
26                     || (isScramble(s1.substring(0, i),
27                             s2.substring(s2.length() - i, s2.length())) && isScramble(
28                             s1.substring(i), s2.substring(0, s2.length() - i)));
29             if (result)
30                 return true;
31         }
32         return false;
33     }
34 }
posted @ 2014-01-05 12:33 Meng Lee 阅读(157) | 评论 (0)编辑 收藏

Given n non-negative integers representing the histogram's bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.
Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3].
The largest rectangle is shown in the shaded area, which has area = 10 unit.
For example,
Given height = [2,1,5,6,2,3],
return 10.

本题需要使用栈维护一个高度单调递增的序列下标,如果遇到一个元素比当前栈顶元素高度小,那么出栈,并计算当前最大面积。如果栈为空,需要特殊考虑。
 1 public class LargestRectangleinHistogram {
 2     public int largestRectangleArea(int[] height) {
 3         Stack<Integer> stack = new Stack<Integer>();
 4         int i = 0;
 5         int maxArea = 0;
 6         int[] h = new int[height.length + 1];
 7         h = Arrays.copyOf(height, height.length + 1);
 8         while (i < h.length) {
 9             if (stack.isEmpty() || h[stack.peek()] <= h[i]) {
10                 stack.push(i++);
11             } else {
12                 int t = stack.pop();
13                 maxArea = Math.max(maxArea, h[t] * (stack.isEmpty() ? i : i - stack.peek() - 1));
14             }
15         }
16         return maxArea;
17     }
18 }
posted @ 2014-01-05 12:31 Meng Lee 阅读(251) | 评论 (0)编辑 收藏

Given a binary tree, return the inorder traversal of its nodes' values.
For example:
Given binary tree {1,#,2,3},
   1
    \
     2
    /
   3
return [1,3,2].
Note: Recursive solution is trivial, could you do it iteratively?

切记p节点初始时指向root.left。代码如下:
 1 public class BinaryTreeInorderTraversal {
 2     public ArrayList<Integer> inorderTraversal(TreeNode root) {
 3         ArrayList<Integer> inOrder = new ArrayList<Integer>();
 4         if (root == null)
 5             return inOrder;
 6         Stack<TreeNode> s = new Stack<TreeNode>();
 7         s.add(root);
 8         TreeNode p = root.left;
 9         while (!s.empty()) {
10             while (p != null) {
11                 s.add(p);
12                 p = p.left;
13             }
14             TreeNode n = s.pop();
15             inOrder.add(n.val);
16             p = n.right;
17             if (p != null) {
18                 s.add(p);
19                 p = p.left;
20             }
21         }
22         return inOrder;
23     }
24 }
posted @ 2014-01-04 11:17 Meng Lee 阅读(147) | 评论 (0)编辑 收藏

Given a collection of integers that might contain duplicates, S, return all possible subsets.
Note:
Elements in a subset must be in non-descending order.
The solution set must not contain duplicate subsets.
For example,
If S = [1,2,2], a solution is:
[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]

由于元素中可能存在重复,因此较之于Subset的实现,需要加一些判断。如果碰到了重复元素,只需要在上一次迭代新增的子集的基础上再进行迭代即可。实现代码如下:
 1 public class SubsetsII {
 2     public ArrayList<ArrayList<Integer>> subsetsWithDup(int[] num) {
 3         ArrayList<ArrayList<Integer>> ret = new ArrayList<ArrayList<Integer>>();
 4         ArrayList<ArrayList<Integer>> lastLevel = null;
 5         ret.add(new ArrayList<Integer>());
 6         Arrays.sort(num);
 7         for (int i = 0; i < num.length; i++) {
 8             ArrayList<ArrayList<Integer>> tmp = new ArrayList<ArrayList<Integer>>();
 9             ArrayList<ArrayList<Integer>> prev = i == 0 || num[i] != num[i - 1] ? ret : lastLevel;
10             for (ArrayList<Integer> s : prev) {
11                 ArrayList<Integer> newSet = new ArrayList<Integer>(s);
12                 newSet.add(num[i]);
13                 tmp.add(newSet);
14             }
15             ret.addAll(tmp);
16             lastLevel = tmp;
17         }
18         return ret;
19     }
20 }
posted @ 2014-01-03 16:40 Meng Lee 阅读(160) | 评论 (0)编辑 收藏

Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, such that:
Only one letter can be changed at a time
Each intermediate word must exist in the dictionary
For example,
Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]
Return
  [
    ["hit","hot","dot","dog","cog"],
    ["hit","hot","lot","log","cog"]
  ]
Note:
All words have the same length.
All words contain only lowercase alphabetic characters.

这个题目应该算是leetcode上比较难的题目了。刚开始我采用了和Word Ladder相似的做法,只是用ArrayList记录了当前变换路径,在小数据的情况下可以Accept,但是大数据超时。究其原因,是由于为每个当前节点记录变换路径的时候,需要复制之前的ArrayList,这个时间开销较大。
其实,我们可以采用一个Map<String, HashSet<String>>结构,记录字典单词的每一个前驱,这样我们可以从end反向遍历,构造出转换路径。
同时,我利用了两个ArrayList,交替记录上一层和下一层的节点,如果下一层节点为空,则不存在路径,立即返回。如果下一层中出现了end,证明找到了所有的最短路径,停止搜索开始构造路径。实现代码如下:
 1 public class WordLadderII {
 2     private void GeneratePath(Map<String, ArrayList<String>> prevMap,
 3             ArrayList<String> path, String word,
 4             ArrayList<ArrayList<String>> ret) {
 5         if (prevMap.get(word).size() == 0) {
 6             path.add(0, word);
 7             ArrayList<String> curPath = new ArrayList<String>(path);
 8             ret.add(curPath);
 9             path.remove(0);
10             return;
11         }
12 
13         path.add(0, word);
14         for (String pt : prevMap.get(word)) {
15             GeneratePath(prevMap, path, pt, ret);
16         }
17         path.remove(0);
18     }
19 
20     public ArrayList<ArrayList<String>> findLadders(String start, String end,
21             HashSet<String> dict) {
22         ArrayList<ArrayList<String>> ret = new ArrayList<ArrayList<String>>();
23         Map<String, ArrayList<String>> prevMap = new HashMap<String, ArrayList<String>>();
24         dict.add(start);
25         dict.add(end);
26         for (String d : dict) {
27             prevMap.put(d, new ArrayList<String>());
28         }
29         ArrayList<HashSet<String>> candidates = new ArrayList<HashSet<String>>();
30         candidates.add(new HashSet<String>());
31         candidates.add(new HashSet<String>());
32         int current = 0;
33         int previous = 1;
34         candidates.get(current).add(start);
35         while (true) {
36             current = current == 0 ? 1 : 0;
37             previous = previous == 0 ? 1 : 0;
38             for (String d : candidates.get(previous)) {
39                 dict.remove(d);
40             }
41             candidates.get(current).clear();
42             for (String wd : candidates.get(previous)) {
43                 for (int pos = 0; pos < wd.length(); ++pos) {
44                     StringBuffer word = new StringBuffer(wd);
45                     for (int i = 'a'; i <= 'z'; ++i) {
46                         if (wd.charAt(pos) == i) {
47                             continue;
48                         }
49 
50                         word.setCharAt(pos, (char) i);
51 
52                         if (dict.contains(word.toString())) {
53                             prevMap.get(word.toString()).add(wd);
54                             candidates.get(current).add(word.toString());
55                         }
56                     }
57                 }
58             }
59 
60             if (candidates.get(current).size() == 0) {
61                 return ret;
62             }
63             if (candidates.get(current).contains(end)) {
64                 break;
65             }
66         }
67 
68         ArrayList<String> path = new ArrayList<String>();
69         GeneratePath(prevMap, path, end, ret);
70 
71         return ret;
72     }
73 }
posted @ 2014-01-02 13:59 Meng Lee 阅读(847) | 评论 (0)编辑 收藏

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 阅读(225) | 评论 (0)编辑 收藏