腾讯2021暑期实习 后台开发 笔试

1. 最小字典序旋转链表

题目

给出一个单向无环链表,其中的节点定义如下:

struct ListNode{
	int val;
	ListNode* next;
}

定义一个链表的旋转链表为:将链表头部的若干连续节点移到尾部。如链表{5,3,2,1,4}的所有旋转链表为{5,3,2,1,4}(自身),{3,2,1,4,5},{2,1,4,5,3},{1,4,5,3,2},{4,5,3,2,1}。
定义链表字典序大小关系为:对于链表a和b,如果存在k,使得对任意i<k,有a_i<b_i,且a_k=b_k,则称a的字典序小于b。其中a_i表示a从头节点开始的第i个后继节点(头节点自身为a_1)。
(其实这里的字典序就和字符串一样,是字面意思。)
完成如下函数,使其返回参数链表的所有旋转链表中字典序最小的一个:

class Solution{
public:
	ListNode* Solve(ListNode* S){
		//Write your code here.
	}
}

如对于链表{5,3,2,1,4},应该返回 {1,4,5,3,2};对于{2,2,5,2,2},应该返回{2,2,2,2,5}。
输入:链表头节点ListNode* s
输出:s的最小字典序旋转链表头节点。
等价题目:POJ 1509

解法

A.暴力

初始设ansS,然后对所有后继节点循环。对每个后继节点temp,依次比较anstemp的所有后继节点,直到val不同或者比较完整个链表。
时间复杂度: O ( n 2 ) O(n^2) O(n2)
我是这么做的,但只过了92.84%的case,然后告知我有段错误。我百思不得其解,唯一能想到的解释是可能没处理好nullptr的情况,但那也不该过这么多case啊……听说有人写这个过了。

B.最小表示法

该算法本身是处理字符串的一个算法,和本题的本质是一样的,下面详述。
任务:对于长度为 n n n的字符串s,将其复制一份并加在尾部,形成长度为 2 n 2n 2n的字符串str。现要在其所有长度为 n n n的子串中找到字典序最小者。
最小表示法能以 O ( n ) O(n) O(n)的时间复杂度完成之。
初始,设i=0, j=1, k=0。这里的ij是即将做比较的可能答案的开始位置,k是从它俩开始的相同的字符串的长度。做如下循环:
如果str[i+k]==str[j+k],则k++
如果str[i+k]<str[j+k],表明str[i:i+k-1]==str[j:j+k-1],所以j, j+1, ..., j+k都不可能是答案的位置,接下来应该比较的位置是ij=j+k+1
如果str[i+k]>str[j+k],与上一种情况相似,所以接下来应该比较的是i+k+1j
代码如下:

int GetMin(string s){
	int l = s.strlen();
	int i = 0, j = 1, k = 0;
	int diff;
	while(i < n && j < n && k < n){
		t = s[(i+k)%n] - s[(j+k)%n];
		if(t==0) k++;
		else{
			if(t>0) i = i+k+1;
			else j = j+k+1;
			if(i==j) j++;//or i++
			k=0;
		}
	}
	return i;
}

时间复杂度: O ( n ) O(n) O(n)

2. 发放广告

题目

n个接收广告的用户,每个人每隔一段时间要接收一条广告,第i个用户的接收间隔为t_i。现在要发送k条广告,请按时间顺序输出接收广告的用户编号。如果某时刻要同时给多个用户发送广告,则按编号从小到大输出。
输入:n+1行。第一行输入nk,后面n行中的第i行为用户i的接收间隔。
输出:k行。第k行为第k个接收广告的用户编号。

解法

A.暴力

设时间变量tik,对其循环:对于所有t[i],计算tik%t[i],如果为0则输出,并增加已投放广告数量,如果数量达到k则跳出;遍历完t[n]后,tik++
时间复杂度:一个很坏的情况下为 O ( n k max ⁡ ( t [ i ] ) ) O(nk\max(t[i])) O(nkmax(t[i]))(只有一个用户,其时间间隔极大)。我尚未证明这是最坏情况,但已经非常糟糕。我如此做,只过了好像70%的case。

B.堆排序

为数组t[n]建立一个最小堆,堆中每个节点表示一个用户,同时具有下次接收广告时间tNext和用户编号no属性。比较节点时,优先比较tNext,相同时比较no

class Node{
public:
	int tNext;
	int no;
	Node* left, right;
	Node(int t_, int no_){
		tNext = t_;
		no = no_;
		left = nullptr;
		right = nullptr;
	}
}
bool operator<(Node* a, Node* b){
	if(a.tNext != b.tNext) return a.tNext < b.tNext;
	return a.no < b.no;
}

每次取出堆顶的最小节点,输出其编号,删除该节点,更新堆,然后添加一个Node(tNext+t[no], no)节点,更新堆。
时间复杂度:建堆和更新堆都需要 O ( n log ⁡ n ) O(n\log n) O(nlogn),共更新 2 k 2k 2k次,总的时间复杂度为 O ( n k log ⁡ n ) O(nk\log n) O(nklogn)

3. 游戏俱乐部

题目

俱乐部里有很多游戏。完成每个游戏,都需要1单位的时间,且都可以得到一定的分数。但是每个游戏都必须在某个时间点之前完成,否则得不到其分数。问如何得到最高分数。
输入:游戏数量n,每个游戏的截止时间t_i,每个游戏的分数s_i
输出:最高可得到的分数。

解法:贪心

对时间点i做循环:在截止时间为i的所有游戏里,完成分数最高的那个。如果没有游戏截止时间为i,则继续在截止时间为i+1的游戏里找,以此类推。找到后令i++,直到i==max(t[n])或者所有游戏都被玩过为止。
时间复杂度: O ( n log ⁡ n ) O(n\log n) O(nlogn)。先把所有游戏按截止时间和分数排序,时间为 O ( n log ⁡ n ) O(n\log n) O(nlogn);然后至多找到n个游戏。(?)

4. 递归相等字符串

题目

有两个长度相等的字符串ab。我们如下定义ab的相等:如果两者的长度为奇数,则要求两者中相同位置的字符都相同(即普通意义上的相等);如果两者的长度为偶数,则将它们都从正中间切成两个字符串,分别是a1, a2b1, b2,要求a1b1a2b2相等,或者a1b2a2b1相等(这里的两个相等是递归定义的)。
输入:数据组数n,和n对长度相等的字符串。
输出:n行,如果一对字符串相等则输出YES,否则输出NO

解法:递归

理论上,按照题中要求递归地实现函数bool Equal(string a, string b)即可。
但是这样会超时,因为生成新字符串消耗了很多时间。改为bool Equal(char* a, char* b, int length)后即可通过。
时间复杂度: O ( n ) O(n) O(n)。因为只有递归到最后一层(字符串长度为奇数)时才逐个比较字符,其他层直接利用更深层的返回值。

5. 打地鼠

题目

有一片m*n大小的场地,其中每个格子都会冒出地鼠。给出矩阵mp[m][n],第i行第j列的格子会每隔mp[i][j]时间冒出一只地鼠(均从0时刻开始)。
再给出时间t,你需要在这段时间里在场地上移动,每次必须在水平或垂直方向移动一格,且不能移到上一时刻所在的格子。如果在某一时刻,你所在的格子有地鼠冒出,则可以打掉地鼠获得1分。在t时刻,如果你不在第m行第n列,则积分清零。
输入:矩阵mp和时间t
输出:时间t过后,最高可得到的分数。

解法:动归

没做出来……待续。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值