回复: 21
跳转到指定楼层
上一主题 下一主题
收起左侧

请教一道国内面试算法题,很短,没做出来

全局:

注册一亩三分地论坛,查看更多干货!

您需要 登录 才可以下载或查看附件。没有帐号?注册账号

x
国内面试题 没做出来...

给一个DNA序列 只包含ACGT四个字符
返回一个整数,这个整数是最短的没出现过的组合

比如 给一个字
您好!
本帖隐藏的内容需要积分高于 188 才可浏览
您当前积分为 0。
使用VIP即刻解锁阅读权限或查看其他获取积分的方式
游客,您好!
本帖隐藏的内容需要积分高于 188 才可浏览
您当前积分为 0。
VIP即刻解锁阅读权限查看其他获取积分的方式
Unlock interview details and practice with AI
Curated Interview Questions from Top Companies
;不"返回1
但是CA没出现过 这个就是最短的 长度2 所以最终返回2

上一篇:Google onsite 時間請問該填什麼時候比較好
下一篇:狗家实习转正面试
推荐
chowhound 2017-10-29 06:40:28 | 只看该作者
全局:
要求只是返回长度 所以并不需要知道具体是哪个没出现。有了input长度直接可以算出来每个长度应该有多少组合,然后每个长度分别是一个set,遍历时 A就扔1级set,AC扔C进1级set,同时AC扔二级set,以此类推,要保存每一级长度set的前一个状态,最后从1级set往上看set的size,到哪级set比理论组合size少 就返回哪级就好了。理论上是n复杂度?
回复

使用道具 举报

推荐
magicsets 2017-10-29 16:40:40 | 只看该作者
全局:
用suffix tree可以时间O(n),空间O(n)。具体就是先O(n)建树,然后从树根逐层BFS做一些判断。不过建树算法非常复杂。

其实看到ACGT,第一反应是压缩索引技术,例如FM-index (burrow wheeler transform),不过对于这一题好像不适用。


估计面试想要的做法是把每个字符压缩为2bit编码,然后用一个32位整数编码子串(可以处理最多4*10^9长度的输入,更长的话要用64位整数)。
在编码后,就不需要用hash set了,因为压缩后的整数分布是稠密的,只需要用一个bitmap。

参考代码如下:

  1. #include <cstddef>
  2. #include <cstdint>
  3. #include <cstdlib>
  4. #include <cstring>
  5. #include <iostream>
  6. #include <string>
  7. #include <vector>

  8. // BitSet的简单实现
  9. class BitSet {
  10. public:
  11.   explicit BitSet(const std::size_t num_bits)
  12.       : data_array_size_((num_bits >> kHigherOrderShift) +
  13.                          (num_bits & kLowerOrderMask ? 1 : 0)),
  14.         data_array_(static_cast<std::size_t*>(std::malloc(data_array_size_ * kDataSize))) {
  15.     std::memset(data_array_, 0, data_array_size_ * kDataSize);
  16.   }

  17.   ~BitSet() {
  18.     std::free(data_array_);
  19.   }

  20.   inline void setBit(const std::size_t bit_num) const {
  21.     data_array_[bit_num >> kHigherOrderShift] |= kTopBit >> (bit_num & kLowerOrderMask);
  22.   }

  23.   inline std::size_t onesCount() const {
  24.     std::size_t count = 0;
  25.     for (std::size_t array_idx = 0; array_idx < data_array_size_; ++array_idx) {
  26.       count += __builtin_popcountl(data_array_[array_idx]);
  27.     }
  28.     return count;
  29.   }

  30. private:
  31.   static constexpr std::size_t kDataSize = sizeof(std::size_t);
  32.   static constexpr std::size_t kLowerOrderMask = (kDataSize << 3) - 1;
  33.   static constexpr std::size_t kHigherOrderShift = kDataSize == 4 ? 5 : 6;
  34.   static constexpr std::size_t kOne = 1ull;
  35.   static constexpr std::size_t kTopBit = kOne << kLowerOrderMask;

  36.   const std::size_t data_array_size_;
  37.   std::size_t *data_array_;
  38. };

  39. int Solve(const std::string &dna) {
  40.   // 编码索引
  41.   std::uint32_t dna2code[128];
  42.   memset(dna2code, 0, sizeof(dna2code));
  43.   dna2code[(int)'a'] = dna2code[(int)'A'] = 0;
  44.   dna2code[(int)'c'] = dna2code[(int)'C'] = 1;
  45.   dna2code[(int)'g'] = dna2code[(int)'G'] = 2;
  46.   dna2code[(int)'t'] = dna2code[(int)'T'] = 3;

  47.   // 编码
  48.   constexpr std::size_t kNumBitsInWord = 32;
  49.   constexpr std::size_t kNumCodesInWord = kNumBitsInWord / 2;

  50.   std::vector<std::uint32_t> words;
  51.   std::size_t shift = 0;
  52.   std::uint32_t word = 0;
  53.   for (const unsigned char c : dna) {
  54.     word |= dna2code[c] << shift;
  55.     if (shift == kNumBitsInWord - 2) {
  56.       words.emplace_back(word);
  57.       shift = 0;
  58.       word = 0;
  59.     } else {
  60.       shift += 2;
  61.     }
  62.   }
  63.   if (shift != 0) {
  64.     words.emplace_back(word);
  65.   }

  66. //  for (const std::uint32_t w : words) {
  67. //    std::cout << std::bitset<32>(w) << "\n";
  68. //  }

  69.   // 枚举长度
  70.   const std::size_t dnaL = dna.length();
  71.   std::size_t subL = 1;
  72.   while (true) {
  73.     const std::size_t max_combinations = 1ull << (subL * 2);
  74.     if (max_combinations > dnaL) {
  75.       break;
  76.     }

  77.     // 遍历子串
  78.     BitSet existence(max_combinations);
  79.     const std::uint64_t sub_mask = max_combinations - 1;

  80.     std::uint64_t buffer = words.front();
  81.     for (std::size_t i = 0; i < words.size() - 2; ++i) {
  82.       buffer |= static_cast<std::uint64_t>(words[i+1]) << kNumBitsInWord;
  83.       for (std::size_t k = 0; k < kNumCodesInWord; ++k) {
  84.         existence.setBit(buffer & sub_mask);
  85.         buffer >>= 2;
  86.       }
  87.     }
  88.     buffer |= static_cast<std::uint64_t>(words.back()) << kNumBitsInWord;
  89.     const std::size_t last = dnaL % kNumCodesInWord;
  90.     const std::size_t num_residual =
  91.         (kNumCodesInWord + (last == 0 ? kNumCodesInWord : last) - subL + 1);
  92.     for (std::size_t k = 0; k < num_residual; ++k) {
  93.       existence.setBit(buffer & sub_mask);
  94.       buffer >>= 2;
  95.     }

  96.     if (existence.onesCount() != max_combinations) {
  97.       break;
  98.     }
  99.     ++subL;
  100.   }
  101.   return subL;
  102. }
复制代码
回复

使用道具 举报

推荐
empy 2017-10-29 07:10:04 | 只看该作者
全局:
yanshuo619 发表于 2017-10-29 06:58
每个元素只是跟前一个状态合起来仍set里,为啥会需要遍历呢
  1. int leastLen(string str) {
  2.         int strLen = str.length();
  3.         int maxLen = log(strLen) / log(4);
  4.         vector<unordered_set<string>> comb(maxLen);
  5.         for (int i = 0; i < strLen; i++)
  6.                 for (int j = 0; j <maxLen && i-j>=0; j++)
  7.                         comb[j].insert(str.substr(i - j, j+1));
  8.         int res = maxLen + 1;
  9.         for (int i = 0; i < maxLen; i++)
  10.                 if (comb[i].size() < pow(4, i + 1))
  11.                         res = i + 1;
  12.         return res;
  13. }
复制代码
时间复杂度 nlog4(n)

补充内容 (2017-10-29 07:13):
最后那个if应该是
                if (comb[i].size() < pow(4, i + 1)) {
                        res = i + 1;
                        break;
                }
回复

使用道具 举报

🔗
wdxh1 2017-10-28 21:35:19 | 只看该作者
全局:
brute force 从长度2开始 生成所有的排列 看看是不是substring 如果都是 长度增加 继续 另外 如果不写code 可以用suffix tree 找一个depth最小 并且child数量小于4的node 就行
回复

使用道具 举报

🔗
 楼主| EddieStallworth 2017-10-29 03:40:14 | 只看该作者
全局:
wdxh1 发表于 2017-10-28 21:35
brute force 从长度2开始 生成所有的排列 看看是不是substring 如果都是 长度增加 继续 另外 如果不写code  ...

对啊 我也是只会暴力解...
回复

使用道具 举报

🔗
k1938slll 2017-10-29 05:03:44 | 只看该作者
全局:
好难?! Google的题吗
回复

使用道具 举报

🔗
fangdanzai 2017-10-29 05:10:27 | 只看该作者
全局:
先把所有可能的组合存在hash里 然后用长度从1到length遍历?
回复

使用道具 举报

🔗
无量塔 2017-10-29 05:35:31 | 只看该作者
全局:
我只能想到类似暴力解的,因为只有4个字母,所以每种length的排列组合数量你都可以直接算出来。
从最小length开始,对于某个length都新建一个hash set,用个sliding window把substring扔进set里,如果set的大小不等于你排列组合算出来的数量就直接返回当前length

补充内容 (2017-10-29 06:22):
打错,不是某个,是每个
回复

使用道具 举报

🔗
zzrdwj 2017-10-29 05:46:01 | 只看该作者
全局:
A for 0, T for 1, C for 2, G for 3,
k tuples----->四进制数字
k=1
while(k){
取k-tuples转化为4进制,得到n-k+1长的数组
for (i=k位四进制的所有数){数组减去i,搜索是否存在0,不存在返回k}
k++
}



补充内容 (2017-10-29 05:53):
n=length(gene)
回复

使用道具 举报

🔗
chowhound 2017-10-29 06:44:46 | 只看该作者
全局:
yanshuo619 发表于 2017-10-29 06:40
要求只是返回长度 所以并不需要知道具体是哪个没出现。有了input长度直接可以算出来每个长度应该有多少组合 ...

前面有一点会产生误会,理论组合size组合数字是根据有多少元素算出来,不是input string那个长度
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册账号
隐私提醒:
  • ☑ 禁止发布广告,拉群,贴个人联系方式:找人请去🔗同学同事飞友,拉群请去🔗拉群结伴,广告请去🔗跳蚤市场,和 🔗租房广告|找室友
  • ☑ 论坛内容在发帖 30 分钟内可以编辑,过后则不能删帖。为防止被骚扰甚至人肉,不要公开留微信等联系方式,如有需求请以论坛私信方式发送。
  • ☑ 干货版块可免费使用 🔗超级匿名:面经(美国面经、中国面经、数科面经、PM面经),抖包袱(美国、中国)和录取汇报、定位选校版
  • ☑ 查阅全站 🔗各种匿名方法

本版积分规则

>
快速回复 返回顶部 返回列表