铭记于心 | ||
---|---|---|
✨我唯一知道的,便是我一无所知✨ |
众所周知,作为一名合格的程序员,算法 能力 是不可获缺的,并且在算法学习的过程中我们总是能感受到算法的✨魅力✨。
☀️短短几行代码,凝聚无数前人智慧;一个普通循环,即是解题之眼☀️
二分,贪心,并查集,二叉树,图论,深度优先搜索(dfs),宽度优先搜索(bfs),数论,动态规划等等, 路漫漫其修远兮,吾将上下而求索! 希望在此集训中与大家共同进步,有所收获!!!
简单的字符串模拟题,简单来说就是翻转前K个字符,前后指针边向中间遍历边交换,可以自定义一个swap(),对于C++来说可以直接用STL的reverse函数
class Solution {
public String reverseStr(String s, int k) {
char[] cs = s.toCharArray();
int n = s.length();
for (int l = 0; l < n; l = l + 2 * k) {
int r = l + k - 1;
reverse(cs, l, Math.min(r, n - 1));
}
return String.valueOf(cs);
}
void reverse(char[] cs, int l, int r) {
while (l < r) {
char c = cs[l];
cs[l] = cs[r];
cs[r] = c;
l++; r--;
}
}
}
2.裁剪数字后查询第k小的数字
数字字符串都是等长的,把索引拼接到数据上。比较大小时,传入。nums[0].length()-queries[i][1]
就是从截取位置之后开始比较。这样好处是在不稳定的排序中后面拼接的索引确保了,每一个数据的唯一性,所以即使不稳定,它也可以正确的找出第k小。这就为选用选择排序铺平了道路。这里我们选择归并排序。
class Solution {
public:
vector<int> smallestTrimmedNumbers(vector<string> &nums, vector<vector<int>> &queries) {
vector<int> ans(queries.size());
int n = nums.size(), m = nums[0].length();
int idx[n];
for (int i = 0; i < queries.size(); ++i) {
auto &q = queries[i];
iota(idx, idx + n, 0);
stable_sort(idx, idx + n, [&](int a, int b) {
auto &s = nums[a], &t = nums[b];
for (int j = m - q[1]; j < m; ++j)
if (s[j] != t[j]) return s[j] < t[j];
return false;
});
ans[i] = idx[q[0] - 1];
}
return ans;
}
};
用两个栈模拟光标的移动,右边的栈,下标顺序是逆序的,这样,往右边放入或取字符时,才可以直接在栈顶位置进行操作,左边的栈顶位置,记得补充一个结束符,这样,在光标左移、右移的操作过程中,需要返回左侧至多10个字符时,可以直接返回子字符串的开始下标,到结束符为止即可输出
#define MAX_STACKSIZE 800000
#define ATMOST_LENGTH 10
#define MIN_VALUE(x, y) (((x) < (y)) ? (x) : (y))
/* leftSize表示左侧栈已用空间,left表示左侧栈,rightSize表示右侧栈已用空间,right表示右侧栈。 */
typedef struct
{
int leftSize;
int rightSize;
char *left;
char *right;
}
TextEditor;
/* 创建对象,并初始化。 */
TextEditor *textEditorCreate()
{
TextEditor *obj = (TextEditor *)malloc(sizeof(TextEditor));
obj->leftSize = 0;
obj->rightSize = 0;
obj->left = (char *)malloc(sizeof(char) * MAX_STACKSIZE);
obj->right = (char *)malloc(sizeof(char) * MAX_STACKSIZE);
return obj;
}
/* 从光标所在位置开始,插入文本。 */
void textEditorAddText(TextEditor *obj, char *text)
{
int x = 0;
/* 此时只需要操作左侧栈。 */
while('\0' != text[x])
{
obj->left[obj->leftSize] = text[x];
obj->leftSize++;
x++;
}
/* 栈顶位置用结束符补充。 */
obj->left[obj->leftSize] = '\0';
return;
}
/* 删除k个字符。 */
int textEditorDeleteText(TextEditor *obj, int k)
{
/* 左侧栈不足k个时,全部删光。 */
int x = MIN_VALUE(obj->leftSize, k);
obj->leftSize -= x;
/* 栈顶位置用结束符补充。 */
obj->left[obj->leftSize] = '\0';
return x;
}
/* 光标向左移动k次。 */
char *textEditorCursorLeft(TextEditor *obj, int k)
{
int index = 0;
/* 每移动一次,左侧栈顶的字符就移动到右侧栈顶。 */
while(0 < k && 0 < obj->leftSize)
{
obj->leftSize--;
obj->right[obj->rightSize] = obj->left[obj->leftSize];
obj->rightSize++;
k--;
}
/* 栈顶位置用结束符补充。 */
obj->left[obj->leftSize] = '\0';
/* 返回左侧长度最多为10的子字符串。 */
if(ATMOST_LENGTH < obj->leftSize)
{
index = obj->leftSize - ATMOST_LENGTH;
}
return &obj->left[index];
}
/* 光标向右移动k次。 */
char *textEditorCursorRight(TextEditor *obj, int k)
{
int index = 0;
/* 每移动一次,右侧栈顶的字符就移动到左侧栈顶。 */
while(0 < k && 0 < obj->rightSize)
{
obj->rightSize--;
obj->left[obj->leftSize] = obj->right[obj->rightSize];
obj->leftSize++;
k--;
}
/* 栈顶位置用结束符补充。 */
obj->left[obj->leftSize] = '\0';
/* 返回左侧长度最多为10的子字符串。 */
if(ATMOST_LENGTH < obj->leftSize)
{
index = obj->leftSize - ATMOST_LENGTH;
}
return &obj->left[index];
}
/* 释放对象。 */
void textEditorFree(TextEditor *obj)
{
free(obj->left);
free(obj->right);
free(obj);
return;
}
#include
using namespace std;
int main() {
int t;
cin >> t;
while(t--) {
int n;
cin >> n;
int k = 0;
while((1 << (k + 1)) <= n - 1) ++k;
for(int i = (1 << k) - 1; i >= 0; i--) {
cout << i << ' ';
}
for(int i = (1 << k); i < n; i++) {
cout << i << ' ';
}
cout << '\n';
}
}
写在最后:
相信大家对今天的集训内容的理解与以往已经有很大不同了吧,或许也感受到了算法的魅力,当然这是一定的,路漫漫其修远兮,吾将上下而求索!伙伴们,明天见!