集合划分问题
题目描述
给定一个数组,每个元素范围是0~K(K < 整数最大值2^32),将该数组分成两部分,使得 |S1- S2|最小,其中S1和S2分别是数组两部分的元素之和。
输入描述
数组元素个数N(N 大于1但不超过 10, 000, 000)。
数组中N个元素(用空格分割)。
输出描述
|S1- S2|的值。
解题思路
转换为 0 − 1 0-1 0−1 背包问题,等价于,问,从 n n n 个数中选,在选取的数的和不超过 n n n 个数的总和的一半的前提下,所能选取的数的和的最大值。
代码实现
typedef long long ll;
int main() {
int n;
scanf("%d", &n);
int a[n + 1];
ll sum = 0, k;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
sum += a[i];
}
k = sum >> 1;
ll dp[k + 1];
memset(dp, 0, sizeof dp);
for (int i = 1; i <= n; i++)
for (ll j = k; j >= a[i]; j--)
dp[j] = max(dp[j], dp[j - a[i]] + a[i]);
printf("%lld", sum - (dp[k] << 1));
return 0;
}
时间复杂度: O ( n × s u m ) O(n \times sum) O(n×sum)。
空间复杂度: O ( s u m ) O(sum) O(sum)。
最长等差数列问题
题目描述
给定一个未排序数组,找出其中最长的等差数列(无需保证数字顺序)。
输入描述
第一行N表示数组中元素个数(N < 10,000,000)
第二行是数组的元素,用空格分割
输出描述
等差序列长度
解题思路
先将数组 a a a 按从小到大排序,令 d p [ i ] [ j ] dp[i][j] dp[i][j] = 公差为 i i i 且以 a j a_j aj 结尾的等差数列的最长长度,则 d p [ i ] [ j ] = m a x ( d p [ i ] [ j ] , d p [ i ] [ a j − i ] + 1 ) dp[i][j]=max(dp[i][j],dp[i][a_j-i]+1) dp[i][j]=max(dp[i][j],dp[i][aj−i]+1)。
上式简化为,由于 d p [ i ] [ j ] dp[i][j] dp[i][j] 不依赖于 d p [ i − 1 ] [ ? ] dp[i-1][?] dp[i−1][?],故可转为一维数组, d p [ j ] = m a x ( d p [ j ] , d p [ a j − i ] + 1 ) dp[j]=max(dp[j],dp[a_j-i]+1) dp[j]=max(dp[j],dp[aj−i]+1)。
代码实现
const int N = 1e5 + 5;
int a[N], dp[N];
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d", &a[i]);
sort(a, a + n);
int diff = a[n - 1] - a[0], res = 1;
int *dpc = dp - a[0];
for (int d = -diff; d <= diff; d++) {
memset(dp, -1, sizeof dp);
for (int i = 0; i < n; i++) {
int pre = a[i] - d;
if (pre >= a[0] && pre <= a[n - 1] && dpc[pre] != -1) {
dpc[a[i]] = max(dpc[a[i]], dpc[pre] + 1);
res = max(res, dpc[a[i]]);
}
dpc[a[i]] = max(dpc[a[i]], 1);
}
}
printf("%d", res);
return 0;
}
时间复杂度: O ( m n ) O(mn) O(mn),其中, m m m 为 a i a_i ai 的最大值与最小值之差。
空间复杂度: O ( n ) O(n) O(n)。
字母组合
题目描述
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合,按照字典序升序排序,如果有重复的结果需要去重给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
输入描述
输入2-9数字组合, 字符串长度 1<=length<=20。
输出描述
输出所有组合。
解题思路
模拟即可。
代码实现
string s, t, all;
unordered_map<int, string> mp = {
{2, "abc"},
{3, "def"},
{4, "ghi"},
{5, "jkl"},
{6, "mno"},
{7, "pqrs"},
{8, "tuv"},
{9, "wxyz"},
};
void dfs(int x) {
if (x >= s.size()) {
all.append(t).append(", ");
return;
}
for (char c: mp[s[x] - '0']) {
t.push_back(c);
dfs(x + 1);
t.pop_back();
}
}
int main() {
cin >> s;
dfs(0);
all.pop_back();
all.pop_back();
cout << "[" << all << "]";
return 0;
}
时间复杂度: O ( n ) O(n) O(n)。
空间复杂度: O ( 1 ) O(1) O(1)。
验证IP地址
题目描述
编写一个函数来验证输入的字符串是否是有效的 IPv4 或 IPv6 地址。
IPv4 地址由十进制数和点来表示,每个地址包含4个十进制数,其范围为 0 - 255, 用(“.”)分割。比如,172.16.254.1;
同时,IPv4 地址内的数不会以 0 开头。比如,地址 172.16.254.01 是不合法的。
IPv6 地址由8组16进制的数字来表示,每组表示 16 比特。这些组数字通过 (“:”)分割。比如, 2001:0db8:85a3:0000:0000:8a2e:0370:7334 是一个有效的地址。而且,我们可以加入一些以 0 开头的数字,字母可以使用大写,也可以是小写。所以, 2001:db8:85a3:0:0:8A2E:0370:7334 也是一个有效的 IPv6 address地址 (即,忽略 0 开头,忽略大小写)。
然而,我们不能因为某个组的值为 0,而使用一个空的组,以至于出现(::)的情况。 比如, 2001:0db8:85a3::8A2E:0370:7334 是无效的 IPv6 地址。
同时,在 IPv6 地址中,多余的 0 也是不被允许的。比如, 02001:0db8:85a3:0000:0000:8a2e:0370:7334 是无效的。
说明: 你可以认为给定的字符串里没有空格或者其他特殊字符。
输入描述
一个IP地址字符串
输出描述
ip地址的类型,可能为:IPv4, IPv6, Neither
解题思路
按题意进行判断即可。
代码实现
bool isIPv4(string &s) {
int idx = 0, cnt = 0;
while (idx < s.size()) {
if (s[idx] == '0' || s[idx] == '.')return false;
int x = 0;
while (idx < s.size() && s[idx] != '.')x = x * 10 + s[idx] - '0', idx++;
if (x > 255)return false;
idx++, cnt++;
}
return cnt == 4;
}
bool isIPv6(string &s) {
int idx = 0, cnt = 0;
while (idx < s.size()) {
if (s[idx] == ':')
return false;
int t = idx;
while (idx < s.size() && s[idx] != ':')idx++;
if (idx - t > 4)return false;
idx++, cnt++;
}
return cnt == 8;
}
int main() {
string s;
cin >> s;
if (isIPv4(s))printf("IPv4");
else if (isIPv6(s))printf("IPv6");
else printf("Neither");
return 0;
}
时间复杂度: O ( n ) O(n) O(n)。
空间复杂度: O ( 1 ) O(1) O(1)。
END
文章文档:公众号 字节幺零二四
回复关键字可获取本文文档。
题目来源:快手2020校园招聘秋招笔试–工程B试卷
文章声明:题目来源 牛客 平台,如有侵权,请联系删除!