什么是左右旋转的数字?
在开始讨论如何找到具有相同左右旋转数字的最长子序列之前,先来了解一下什么是左右旋转的数字。
左右旋转数字是指将一个数字的前面若干位移到最后面,得到的新数字和原数字是相同的,比如说:
1234左旋一位得到2341
1234右旋一位得到4123
左旋和右旋我们可以通过对数字进行字符串处理来实现,下面看代码:
// 左旋
string leftRotate(string s, int n) {
if (n >= s.size()) {
return s;
}
return s.substr(n) + s.substr(0, n);
}
// 右旋
string rightRotate(string s, int n) {
if (n >= s.size()) {
return s;
}
return s.substr(s.size() - n) + s.substr(0, s.size() - n);
}
如何找到具有相同左右旋转的数字的最长子序列?
我们可以先将数组进行排序,然后根据题目要求,要找到具有相同左右旋转的数字,就需要考虑数字的特点,即数字旋转之后仍然相同。
我们可以将所有数字旋转两次,比如对于数字1234,可得到以下6个数字:
1234
2341
3412
4123
2341
3412
我们可以发现,如果将这6个数字进行排序,那么具有相同左右旋转的数字会被排在一起,如下所示:
1234
2341
2341
3412
3412
4123
我们可以对这个排序后的数字序列进行遍历,维护一个最长子序列长度即可,下面看代码:
int longestSubsequence(vector& nums) {
unordered_map used;
for (int i = 0; i < nums.size(); i++) {
used[nums[i]] = false; // 初始化
}
sort(nums.begin(), nums.end());
int res = 0;
for (int i = 0; i < nums.size(); i++) {
if (!used[nums[i]]) { // 当前数字未出现在前面
int j = i + 1, k = i + 2; // 从后面取两个数字
while (k < nums.size()) {
if (nums[i] == nums[j] && nums[i] == nums[k]) { // 找到一个可能的子序列
used[nums[j]] = used[nums[k]] = true;
j += 1, k += 2; // 继续往后找
} else {
j += 1, k += 1;
}
}
used[nums[i]] = true; // 标记该数字已经被使用
res = max(res, k - i); // 更新子序列长度
}
}
return res;
}
时间复杂度分析
我们需要将数组排序,所以时间复杂度为O(nlogn),遍历数组的时间复杂度为O(n),因此总时间复杂度为O(nlogn)。
结果验证
我们来验证一下代码的正确性,以数组[10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 1, 11, 21, 31, 41, 51, 61, 71, 81, 91]为例,排序后得到:
1
10
100
11
20
21
30
31
40
41
50
51
60
61
70
71
80
81
90
91
从这里我们可以找到三个子序列,它们的长度都为5,分别是 [1, 10, 100, 11, 20],[20, 21, 30, 31, 40],[50, 51, 60, 61, 70],代码的输出也是5,因此可以得到正确的结果。
总结
本篇文章主要讨论了如何找到具有相同左右旋转数字的最长子序列,首先介绍了何为左右旋转数字,然后讲述了实现思路,最后给出了完整的代码。通过本篇文章的学习,我们可以更好地掌握对于这一类问题的解题思路,提高我们的算法水平。