技术 / 算法 · 2017年3月23日 1

strikingly从面试获取环节到面试

成功的获取了面试资格但最终因为种种原因还是倒在了面试上(紧张的不得了,得治),不过依然是一次很有收获的经历也以此作为总结以备战下一次面试。

面试资格获取环节

show me the code

题目

https://github.com/joycehan/strikingly-interview-test-instructions/tree/new

概述: 以你应聘的职位为基准,用相应的语言编写一套程序调用其提供的api自动的去玩hangman,每次最多猜测80个单词并且每个单词最多猜错10次,然后根据你总共的猜错的次数和猜对的单词进行一次评分,平均分在1000分左右。

解题思路

算法:决策树

准确率:  选用的辞典中的单词与它选用的单词重合率越高其准确率越高

关键点:单词的长度,字母在单词中出现的位置频率。

将辞典下单词转化成树结构的步骤

  1. 对其辞典中的单词首先按单词长度进行分类。
  2. 选择一个长度分类对其下的所有单词进行一次a-z的字母出现频率的统计。
  3. 对分类中频率出现最高的字母与其位置为依据再进行一次分类。
  4. 排除掉该频率最高的字母,对剩下的字母出现频率进行统计。
  5. 重复3,4的步骤直到没有匹配的字母为止。
  6. 对剩下的长度重复3,4,5的步骤。

比如hello、world、copy、gaogao这4个单词将会被转化为图1,途中的0节点则表示已经没有匹配的字母了。

图1

猜词策略

由于猜词只能一个一个字母猜测不管对错你都能获得该单词的长度,如果猜对字母则会提示你这个字母。比如猜测单词为hello的时候你首先得到的会是*****这类形式的提示,若猜中一个字符比如l将会得到**ll*的提示。若没猜中依旧得到*****这个提示。

猜测步骤

  1. 得到猜测的单词长度。
  2. 根据长度在决策树中找到该长度的节点。
  3. 在找到的长度节点下取出字母(注:频率出现最高的字母)进行猜测。
  4. 未命中,取出该节点下的第一个节点上的字母进行猜测,命中则进入到步骤5,未命中则继续进入到下一个节点重复执行步骤4,若节点到达最底部或单词容错次数达到最大则跳过该单词继续下一个单词再重新回到步骤1。
  5. 命中,从提示中得到该字母出现的位置和字母作为依据查询。比如上面的**ll*的提示,则得到l(2,3),然后去找到这个节点。(注: 第一个位置标注为0所以l出现的位置分别是2,3)
  6. 从这个节点下面继续取出字母进行猜测(注:该节点下频率出现最高的字母),若单词全部命中或节点下已经没有节点则回到步骤1,未命中执行步骤4,命中则执行步骤5。

优化手段

对命中的单词进行收集,收集到一定的数量时候转化成决策树猜测来提升准确率。

面试环节

module与class的区别(回答的不完整)

正确答案↓

Ruby的class关键字更像是一个作用域操作符,而不是类型声明语句。class关键字的核心任务是把你带到类的上下文中,让你可以在里面定义方法。

每个类都是一个模块,类就是带有三个方法(new,allocate,superclass)的增强模块,通过这三个方法可以组织类的继承结构,并创建对象

Ruby中的类和模块的概念十分接近,完全可以将二者相互替代,之所以同时保留二者的原因是为了保持代码的清晰性,让代码意图更加明确。使用原则:

希望把自己代码包含(include)到别的代码中,应该使用模块 希望某段代码被实例化或被继承,应该使用类

模块机制可以用来实现类似其它语言中的命名空间(Namespace)概念

什么是duck typing(回答正确)

正确答案↓

鸭子类型是动态语言的一种风格,这种风格下对象没有有效的语义,是由当前的属性和集合决定。这个概念源自鸭子测试。 当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。 鸭子类型通常得益于不测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用。 简单的说鸭子类型关注的不是对象本身,而是方法集的使用(个人感觉和弱类型的用意很像)。

monkey patch(重点偏离)

正确答案↓

如果你粗心地为某个类添加了新功能,同时覆盖了类原来的功能,进而影响到其他部分的代码,这样的patch称之为猴子补丁(Monkeypatch)

after_save和after_commit的区别(回答出错)

正确答案↓

参考这篇文章 Five More Active Record Features You Should Be Using

里面讲到了after_commit 和 after_save 的区别,如果 save 操作是在一个 transaction 中进行的,即使 transaction 失败回滚,after_save 依然会触发,但 after_commit 不会,它只有当数据真正保存到数据库才会触发。

attr_accessible有什么作用(回答错误)

正确答案↓

指定的属性可以被访问,而其他属性将会被设置为protected,和attr_protected相反下作用。

class User < ActiveRecord::Base
  attr_accessible :name, :bio
end

rails3 mime_type的实现(没回答出来)

正确答案↓

可能问题记错了,待找。

lamda,proc,block的区别( 回答不完整 )

正确答案↓

参考:http://awaxman11.github.io/blog/2013/08/05/what-is-the-difference-between-a-block/

  • Proc和Lambda都是对象,而Block不是
  • 参数列表中最多只能有一个Block,但是可以有多个Proc或Lambda
  • Lambda对参数的检查很严格,而Proc则比较宽松
  • Proc和Lambda中return关键字的行为是不同的

view模版优化(回答到点子上了但不完整)

正确答案↓

使用page cache比如View 中用的cache 该方法来自ActionView::Helpers::CacheHelper

例子如下: 如果我们在View 中写下如下代码

<% cache ["article",@article.title,@article.content] do %>
  <%= @article.title %>
  <%= @article.content %>
<% end %>

show me the code

斐波那契数列的定义:f(n) = f(n-1)+f(n-2)。

f(n-3)/f(n-2)-f(n-1)/f(n) < t

t取值范围: 0<t<1

当t越趋近于无穷小的时候(比如t^-6),那么f(n-1)/f(n)就越接近黄金比例。

要求实现输入一个t,当f(n-3)/f(n-2)-f(n-1)/f(n)小于这个的t的时候返回f(n-1)/f(n)。

时间: 10分钟

实现的版本(ruby):

def fibonacci(n)
  return n if n<=1
  return fibonacci(n-1)+fibonacci(n-2)
end

def practice(t)
  (3...99999999).to_a.each do |n|
    v = fibonacci(n-2)/Float(fibonacci(n-1))-fibonacci(n-1)/fibonacci(n)
    return v if v.abs < t
  end
end

优化手段

空间换时间,将fibonacci计算的出来的值保存在容器里。比如存储的方式是以hash存储,那么可以将斐波那契的项数作为key,对应的数作为值,进行查询的时候只需要传入key就能得到值,不用再每次循环都去递归计算,随着循环次数和数值增大其优化就越明显。