//使用哈希表来存储目标值与当前元素的差,如果未来某个值对于该差值,则找到一个结果。返回。
class Solution{
public:
vector twoSum(vector &nums, int target){
unordered_map hashTab;
for (int i = 0; i < nums.size(); ++i)
{
if (hashTab.find(nums[i]) != hashTab.end()) //如果哈希表中存在当前值
return {hashTab[nums[i]], i};
hashTab[target - nums[i]] = i;
}
return {};
}
};
//模拟加法过程,和链表操作。
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode *head = l1, *pre = l1;
int carry = 0;
while (l1 && l2){ //同时遍历两个链表
int sum = l1->val + l2->val + carry;
carry = sum / 10, l1->val = sum % 10;
pre = l1, l1 = l1->next;
l2 = l2->next;
}
if (!l1) //原来存储结果的链表完了,将另一个链表剩余部分作为存放结果的地方。
pre->next = l2, l1 = l2;
while (l1){
int sum = l1->val + carry;
carry = sum / 10, l1->val = sum % 10;
pre = l1, l1 = l1->next;
}
if (carry) //有进位
pre->next = new ListNode(1);
return head;
}
};
//设立一个数组来存储当前窗口中各元素的个数,当新加入的元素导致其出现个数超过2,需要移动左边界,使窗口内的各元素出现次数为1.
//leetcode使用的固定左边界,扩展右边界。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int left = 0, right = 0, len = 0;
vector cnt(128, 0);
while (right < s.size()) {
cnt[s[right]]++;
while (cnt[s[right]] > 1)//查找值等于当前元素的位置,左边界移到下一位置,使窗口内各字符不会重复出现
cnt[s[left++]]--;
right++;
len = max(len, right - left);
}
return len;
}
};
//依次合并两个数组,直到到达合并数组的中间时,可以直接求得中位数。
class Solution {
public:
double findMedianSortedArrays(vector &nums1, vector &nums2){
int i = 0, j = 0, cnt = 0;
int m = nums2.size(), n = nums1.size();
int mid = (m + n - 1) / 2;
while (cnt < mid && i < n && j < m){ //找中位数的位置,丢弃前(m+n)/2-1个元素
if (nums1[i] < nums2[j])
i++;
else
j++;
cnt++;
}
if (cnt < mid){ //一个数组遍历完
while (cnt < mid){
i == n ? j++ : i++;
cnt++;
}
}
double x;
if ((m + n) % 2){ //根据数组个数的奇偶性确定中位数。
if (i == n)
x = nums2[j];
else if (j == m)
x = nums1[i];
else
x = min(nums1[i], nums2[j]);
}
else{
if (i == n)
x = nums2[j] + nums2[j + 1];
else if (j == m)
x = nums1[i] + nums1[i + 1];
else{//需要找接下来最小的两个数。
int min1 = min(nums1[i], nums2[j]), min2;
if (nums1[i] < nums2[j])
i++;
else
j++;
if (i == n)
min2 = nums2[j];
else if (j == m)
min2 = nums1[i];
else
min2 = min(nums1[i], nums2[j]);
x = min1 + min2;
}
x /= 2;
}
return x;
}
};
//相同的方法,更少的代码。
class Solution {
public:
double findMedianSortedArrays(vector& nums1, vector& nums2) {
int left, right, i = 0, j = 0, k = 0; //left和right用来存放第(m+n)/2和第(m+n)/2+1小的值
int n = nums1.size(), m = nums2.size();
while (k <= (m + n) / 2) {
left = right;
if (i < n && (j >= m || nums1[i] < nums2[j]))
right = nums1[i++];
else
right = nums2[j++];
k++;
}
if ((m + n) & 1)
return right;
else
return (left + right) / 2.0;
}
};
//该题为目标为找出正确的划分线。可以选择在小的数组进行查找划分的位置,
//方法:堆栈使用的典型案例,左括号压入,遇到右括号弹出。其他情况无效。
#define ISLEFT(c) (c == '(' || c == '{' || c == '[')
#define ISMATCH(a, b) (a == '(' && b == ')' || a == '{' && b == '}' || a == '[' && b == ']')
#define MAXSIZE 10000
struct stack {
char data[MAXSIZE/2 + 1];
int top;
};
// 输入s [1,10000]
bool isValid(char *s)
{
struct stack st;
st.top = -1;
while (*s){
if (ISLEFT(*s) && st.top < MAXSIZE) //输入为左括号,且小于栈的大小
st.data[++st.top] = *s;
else if (st.top != -1 && ISMATCH(st.data[st.top], *s)) //输入右括号且与栈顶匹配
st.top--;
else //在字符串未读完的情况下,不可能匹配成功。
return false; //break;
s++;
}
return st.top == -1; //栈清空,字符串全匹配
}
//方法:依次比较两个链表的头,指向值小的链表,该链表指针往后移一位,直到有个链表遍历完。
#include
#include
struct ListNode {
int val;
struct ListNode *next;
};
struct ListNode *mergeTwoLists(struct ListNode *l1, struct ListNode *l2)
{
//增加头节点,不需要单独处理开始节点。
struct ListNode *head = (struct ListNode *)malloc(sizeof(struct ListNode));
head->next = NULL;
struct ListNode *p = head;
while (l1 && l2){
if (l1->val > l2->val){
p->next = l2;
l2 = l2->next;
}else{
p->next = l1;
l1 = l1->next;
}
p = p->next;
}
if (l1)
p->next = l1;
else
p->next = l2;
p=head->next;
free(head);
return p;
}
//递归方法
struct ListNode *mergeTwoLists(struct ListNode *l1, struct ListNode *l2)
{
if (!l1)
return l2;
if (!l2)
return l1;
if (l1->val < l2->val){ //当前l1小,则头应该为l1,让接下来的l1和l2比较。
l1->next = mergeTwoLists(l1->next, l2);
return l1;
}
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
/*
//根据数组创建无头链表
struct ListNode *creat(int *array, int size)
{
struct ListNode *q, *head = NULL;
int i = 0;
while (i < size){
struct ListNode *p = (struct ListNode *)malloc(sizeof(struct ListNode));
p->val = array[i++];
p->next = NULL;
if (!head)
head = p, q = p;
else
q->next = p;
q = p;
}
return head;
}
void print(struct ListNode *p)
{
printf("p=[");
while (p){
printf("%d,", p->val);
p = p->next;
}
printf("]\n");
}
int main()
{
freopen("data.txt", "r", stdin);
struct ListNode *p[2];
int N[2];
//vscode的launch.json中的cwd不是当前工作目录,"D:\\mingw64\\bin",使得freopen("data.txt","r",stdin)出错。使用gcc test.c;a.exe却不会出错。
while (scanf("%d %d",&N[0],&N[1]) != EOF) {
int arr[2][30];
for (int i = 0; i < 2; ++i){
int j = 0;
while (j < N[i])
scanf("%d", &arr[i][j++]);
p[i] = creat(arr[i], N[i]);
print(p[i]);
}
print(mergeTwoLists(p[0], p[1]));
}
fclose(stdin);
return 0;
}*/
//方法:只有当当前字符串满足(左括号数量>=右括号数量),才有可能有效,当左右括号都用完了就得到一条有效字符串。
void generate_str(int i, int j, int *k, int n, char *stack, char **result)
{
if(i<j) return;//当前字符串,左括号数量少于右括号数量,则这个字符串肯定不满足要求;
if (i < n){
stack[i + j] = '(';
generate_str(i + 1, j, k, n, stack, result);
}
if (j < n){ //if(ij) 表示当左括号多余右括号,才可以添加右括号。
stack[i + j] = ')';
generate_str(i, j + 1, k, n, stack, result);
}
if (i == j&&i==n){ //所有括号添加完毕,
char *s = (char *)malloc(sizeof(char) * n * 2 + 1);
strcpy(s, stack);
result[*k] = s;
*k = *k + 1;
}
}
char **generateParenthesis(int n, int *returnSize)
{
int left = 0, right = 0;
char *s = (char *)malloc(sizeof(char) * n * 2 + 1);
char **result = (char **)malloc(sizeof(char *) * n);
s[n * 2] = 0;
generate_str(0, 0, returnSize, n, s, result);
free(s);
return result;
}
//方法:由各个链表的当前指针构成最小堆,然后在推根节点的链表指向下一位(不为空)或用堆最后一个节点的链表代替(为空),然后调整堆。直到堆为空。
//还可以用桶排序。分而治之,递归,依次合并两个
#define MAXSIZE 10000
#define MAXVALUE 10001
//调整第i个节点为根的子堆,
void heapAdjust(struct ListNode** heap, int size, int i)
{
heap[0] = heap[i];
int son = i * 2;
while (son <= size) {
if (son < size && heap[son + 1]->val < heap[son]->val) //比较两个子节点的大小
son++;
if (heap[0]->val > heap[son]->val) { //比较最小子节点和父节点的大小
heap[son / 2] = heap[son];
son *= 2;
} else //子树满足最小堆的要求
break;
}
heap[son / 2] = heap[0];
}
struct ListNode* mergeKLists(struct ListNode** lists, int listsSize)
{
//int *p[10] 指针数组,10个int指针构成的数组,int a[3][10]; p[1]=a[1],p++指向a[1][1]
//int (*p)[10] 数组指针,指向数组长度为10的指针,p=a;p++指向a[1]
struct ListNode* heap[MAXSIZE + 1] = { NULL }; //初始化指针,
struct ListNode* head = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* p = head;
int size = 0;
for (int i = 0; i < listsSize; ++i) {
if (lists[i])
heap[++size] = lists[i];
}
//从有子节点的点开始建最小堆
for (int i = size / 2; i > 0; --i)
heapAdjust(heap, size, i);
while (size > 1) {
p->next = heap[1];
p = heap[1];
if (!heap[1]->next) { //链表遍历完,由堆里最后一个链表替代。堆大小减一
heap[1] = heap[size];
size--;
} else
heap[1] = heap[1]->next;
heapAdjust(heap, size, 1);
}
p->next = heap[1];
p = head->next;
free(head);
return p;
}
//方法:先前指针q、当前指针p、下一个指针p->next,修改三者的指向即可。如1-2-3-4
struct ListNode* swapPairs(struct ListNode* head)
{
struct ListNode* p = head;
struct ListNode* q = (struct ListNode*)malloc(sizeof(struct ListNode));
q->next = head;
head = q;
while (p) {
if (p->next) { //head(q)-1(p)-2-3-4
q->next = p->next; //head(q)-2-3-4 1(p)-2-3-4
p->next = p->next->next; //1(p)-3-4 head(q)-2-3-4
q->next->next = p; //head(q)-2-1(p)-3-4
q = p;
p = p->next;
} else
break;
}
p = head->next;
free(head);
return p;
}
//方法:之前指针、当前指针,边遍历边反转。当满足k步后,连接上一段和该段;不满k步,再反转回去。
//另外可以先得长度为k的链表,再进行反转。
struct ListNode* reverseKGroup(struct ListNode* head, int k)
{
if (k < 2 || !head || !head->next)
return head;
struct ListNode* p = (struct ListNode*)malloc(sizeof(struct ListNode));
p->next = head;
struct ListNode* start = p;
while (head) {
struct ListNode *pre = NULL, *begin = head;
int i = 0;
while (i < k && head){ // 每前进一步都进行反转
struct ListNode* tmp = head->next;
head->next = pre;
pre = head;
head = tmp;
++i;
}
if (i == k) { //长度满足,更新前起始节点
start->next = pre;
start = begin;
} else if (!head) { //长度不满足,再次反转最后一段。
head = pre;
pre = NULL;
while (head) {
struct ListNode* tmp = head->next;
head->next = pre;
pre = head;
head = tmp;
}
start->next = pre;
}
}
head = p->next;
free(p);
return head;
}
class Solution {
public:
vector findSubstring(string s, vector& words) {
unordered_map> wordsSet;
int num = 0;
for (auto w : words){ //统计各词的出现次数,并将每个词对应一个整数
if (wordsSet.find(w) != wordsSet.end())
wordsSet[w][0]++;
else
wordsSet[w] = {1, num++};
}
int n = s.size(), m = words[0].size(), k = words.size();
vector vis(num), ans; //vis每个词出现的次数
for (int i = 0; i <= n - m * k; ++i){
auto sub = s.substr(i, m);
if (wordsSet.find(sub) != wordsSet.end()){
for (auto w : wordsSet)
vis[w.second[1]] = w.second[0];
int cnt = k, j = i;
while (wordsSet.find(sub) != wordsSet.end() && vis[wordsSet[sub][1]] > 0){
vis[wordsSet[sub][1]]--;
j = j + m;
sub = s.substr(j, m);
cnt--;
}
if (cnt == 0)//所有词都出现
ans.push_back(i);
}
}
return ans;
}
};
//还可以从第一个单词的每个位置开始,一次移动每个单词的长度,这样在后续比较时,可以跳过一些不可能匹配的子串。
//记录当前还有多少个左括号等待匹配,第i个位置上的值表示当前已经匹配的括号数。长度的更新只发生在出现右括号的时候
class Solution {
public:
int longestValidParentheses(string s) {
int i = 0, n = s.size(), st = 0;
vector preAns(n, 0);//存储已经匹配了括号数
int maxLen = 0;
for (int i = 0; i < n; ++i){
if (s[i] == ')'){
if (st == 0){
preAns[0] = 0;//没有左括号匹配,重新开始
} else { //有匹配的括号
preAns[st - 1] += preAns[st] + 2; //之前已经匹配的个数+后面匹配的个数+最新匹配的两个
preAns[st--] = 0;
maxLen = fmax(maxLen, preAns[st]);
}
}
else
st++;
}
return maxLen;
}
};
//leetcode的方法更简洁;可以指比较左右括号的个数,右括号多,则一定不满足,相等则一定匹配,为了求左括号多的情况,反向遍历
class Solution {
const int n = 9;
int m;
vector> vis; //vector> vis(27,vector(10,false));会出错
vector> arr;
bool dfs(vector> &board, int ii){
if (ii == m)
return true;
int i = arr[ii].first, j = arr[ii].second;
for (int k = 1; k < 10; ++k){
int x = i / 3, y = j / 3;
int i1 = x * 3 + y + 18, i2 = i, i3 = j + 9;
if (!vis[i1][k] && !vis[i2][k] && !vis[i3][k]){
vis[i1][k] = 1, vis[i2][k] = 1, vis[i3][k] = 1;
board[i][j] = '0' + k;
if (dfs(board, ii + 1))
return true;
vis[i1][k] = 0, vis[i2][k] = 0, vis[i3][k] = 0;
}
}
return false;
}
public:
void solveSudoku(vector>& board) {
vis.assign(27, vector(10, false));
for (int i = 0; i < n; ++i){
for (int j = 0; j < n; ++j){
if (board[i][j] != '.'){
int x = i / 3, y = j / 3, k = board[i][j] - '0';
vis[x * 3 + y + 18][k] = 1;
vis[i][k] = 1;
vis[j + 9][k] = 1;
}
else
arr.emplace_back(i, j); //emplace_back()原地构造,不需要触发拷贝构造和转移构造。
}
}
m = arr.size();
dfs(board, 0);
}
};
//在原数组上修改。用来记录某个数是否出现过。
class Solution {
public:
int firstMissingPositive(vector& nums) {
int MinInt = 1, n = nums.size();
for (int i = 0; i < n; ++i){
if (nums[i] < 1)
nums[i] = (n + 1);
}
for (int i = 0; i < n; ++i){
int k = abs(nums[i]) - 1;
if (k < n)
nums[k] = -abs(nums[k]);
}
for (int i = 0; i < n; ++i){
if (nums[i] > 0)
return i + 1;
}
return n + 1;
}
};
//围绕中心点逐步旋转
class Solution {
public:
void rotate(vector>& matrix) {
int n=matrix.size();
for(int i=0;i
class Solution {
public:
ListNode *reverseBetween(ListNode *head, int left, int right) {
if (left == right)
return head;
int i = 1;
ListNode h(0, head);
ListNode *p = head, *p_l = &h, *pre = &h;
while (i < left) { //找到left对应的节点p
pre = p;
p = p->next;
i++;
}
p_l = pre;//不进行翻转的前一个节点
ListNode *p_r = p;//进行翻转的第一个节点,
pre = p, p = p->next, i++;
while (i <= right) { //需要翻转的节点
ListNode *q = p->next;
p->next = pre;
pre = p, p = q;
i++;
}
p_l->next = pre, p_r->next = p;//将left前面的链表、翻转链表、right后面的链表 连接起来
return h.next;
}
};
/*
for (int i = 0; i < right - left; i++) {
next = cur->next;
cur->next = next->next;
next->next = pre->next;
pre->next = next;
}
*/
//可以进一步优化,只需要两行的dp,存储上一行和当前行;dp[i][j]表示字符串s的[:,i]里匹配t[:j]的子序列个数
class Solution {
public:
int numDistinct(string s, string t) {
int n = s.size(), m = t.size();f
if (n < m)
return 0;
vector> dp(2, vector(n + 1, 0));
for (int i = 0; i <= n; ++i)
dp[0][i] = 1;
int row, prerow = 0;
for (int i = 1; i <= m; i++) {
row = i & 1;
dp[row][i - 1] = 0;
for (int j = i; j <= n; j++) {
cout << "(" << t[i - 1] << "," << s[j - 1] << "),";
if (t[i - 1] == s[j - 1]) { //i-1和j-1新匹配个数+之前已经匹配的个数
dp[row][j] = dp[row][j - 1] + dp[prerow][j - 1];
} else { //不产生新匹配的子序列,
dp[row][j] = dp[row][j - 1];
}
}
prerow = row;
}
return dp[row][n];
}
};
//方法:杨辉三角为二项式(两个数之和的整数次幂)的系数,有规律,组合。
//也可以一层一层的算。
int *getRow(int rowIndex, int *returnSize)
{
int *ans = (int *)malloc(sizeof(int) * (rowIndex + 1));
ans[0] = ans[rowIndex] = 1;
for (int i = 1; i < rowIndex; ++i){
if (i <= rowIndex / 2)
ans[i] = (long long)ans[i - 1] * (rowIndex - i + 1) / i; //注意运算可能溢出。
else
ans[i] = ans[rowIndex - i];
}
*returnSize = rowIndex + 1;
return ans;
}
class Solution {
int maxSum = -2000;
int dfs(TreeNode *root, int sum) {
if (root == nullptr)
return 0;
sum += root->val;
maxSum = max(sum, maxSum); //某条路径执行到当前点的最大值
sum = max(sum, 0);
int leftSum = dfs(root->left, sum);
leftSum += root->val;
leftSum = max(0, leftSum);
sum = max(sum, leftSum); 左边子树某点出发经过该点
int rightSum = dfs(root->right, sum);
rightSum += root->val;
rightSum = max(0, rightSum);
return max(rightSum, leftSum); //经过该点返回的最大值
}
public:
int maxPathSum(TreeNode *root) {
dfs(root, 0);
return maxSum;
}
};
/*更简洁的版本
int dfs(TreeNode *root) {
if (root == nullptr)
return 0;
int leftSum = dfs(root->left);
int rightSum = dfs(root->right);
leftSum = max(leftSum, 0);
rightSum = max(rightSum, 0);
int sum = leftSum + rightSum + root->val;
maxSum = max(sum, maxSum);
return max(leftSum, rightSum) + root->val;
}*/
//使用递归,不断尝试不同长度的分割(从0当前位置到n最后一个位置),还可以将求回文串标志加到搜索里面。
//记忆化搜索,需要另一个函数来求标志的值,会出现递归调用的情况,当复杂度一样的,
class Solution{
vector tmp;
vector> flag; //位置i到j是否为回文串。
vector> ans;
int n;
void dfs(int i, string &s){
if (i == n){
ans.push_back(tmp);
return;
}
for (int j = i; j < n; ++j){
if (flag[i][j]){
tmp.push_back(s.substr(i, j - i + 1));
dfs(j + 1, s);
tmp.pop_back();
}
}
}
public:
vector> partition(string s){
n = s.size();
flag.assign(n, vector(n, true));
//因为我们会用到ij中间(i+1到j-1)的字符串是否匹配。所以必须先求之间是否回文。三角形范围从小到大
for (int i = n - 1; i >= 0; --i){ //矩阵的右下角
for (int j = i + 1; j < n; ++j)
flag[i][j] = (s[i] == s[j]) && flag[i + 1][j - 1];
}
//for (int i = 0; i =0; --j)
// flag[i][j] = (s[i] == s[j]) && flag[i - 1][j + 1];
//}
dfs(0, s);
return ans;
}
};
//同上,使用相同的方法求所有i到j是否为回文串。然后使用DP[i]表示0-i分割的最小次数,由0-i是否为回文串,DP[i]=min{DP[j]}+1(不是)或0(是);
//从左往右依次新加一个字符s[i],求DP[i],之前位置上DP已经求得,新加的s[i]可能使得DP[i]=min{DP[j]}+1;
class Solution {
public:
int minCut(string s){
int n = s.size();
vector> f(n, vector(n, true));
for (int i = n - 1; i >= 0; --i){
for (int j = i + 1; j < n; ++j){
f[i][j] = s[i] == s[j] && f[i + 1][j - 1];
}
}
vector ans(n, INT_MAX);
for (int i = 0; i < n; ++i){
if (f[0][i]){
ans[i] = 0;
continue;
}
for (int j = 0; j < i; ++j){
if (f[j + 1][i])
ans[i] = min(ans[i], ans[j] + 1);
}
}
return ans[n - 1];
}
};
//对于节点i,我们只需统计i+1到n的节点与i构成的线,取点数最大值的线。不需要考虑i之前的点,因为之前通过i和之前的点的线已经考虑过了。
//新加一个点i,需要遍历之前节点,确定到线,线上点数加一,注意:去重。
class Solution {
int n;
int max_lines_contains_i(vector> &points, int i) {//求在一条线上的点数最大的值。
unordered_map line;
int duplicate = 0, verti = 1;
int maxp = 0;
int x1 = points[i][0], y1 = points[i][1];
for (int j = i + 1; j < n; ++j) {
int x2 = points[j][0], y2 = points[j][1];
if (x1 == x2 && y1 == y2)
duplicate++;
else if (x1 == x2) {
verti++;
} else {
double scope = 1.0 * (y1 - y2) / (x1 - x2);
if (line.count(scope))
line[scope]++;
else
line[scope] = 2;
maxp = max(line[scope], maxp);
}
}
return max(maxp, verti) + duplicate;
}
public:
int maxPoints(vector> &points) {
n = points.size();
if (n < 3)
return n;
int maxp = 1;
for (int i = 0; i < n - 1; ++i) {
maxp = max(max_lines_contains_i(points, i), maxp);
}
return maxp;
}
};
//没有考虑单操作数,如3-(-(1+1)) 3 1 1 + - - 当作非法输入
class Solution {
public:
int evalRPN(vector& tokens) {
stacknum;
for (auto s : tokens) {
if (isdigit(s[s.size() - 1])) {//数可能为负数
int n = stoi(s);
num.push(n);
}
else {
int n1, n2, x;
n2 = num.top(), num.pop();
n1 = num.top(), num.pop(); //后弹出的为前一个操作数
if (s == "+")x = n1 + n2;
else if (s == "-")x = n1 - n2;
else if (s == "*")x = n1 * n2;
else x = n1 / n2;
num.push(x);
}
}
return num.top();
}
};
//官方代码更简洁
class Solution{
public:
int calculate(string s){
int n = s.size();
int localSum = 0;
stack num;
stack ope;
for (int i = 0; i < n; ++i){
if (isdigit(s[i])){
int k = 0;
while (isdigit(s[i])){
k = k * 10 + (s[i] - '0'); //加括号防止超出int类型范围
i++;
}
if (ope.empty() || ope.top() == '(')//前面没有操作符
localSum += k;
else { //前面有操作符,需要删掉操作符
if (ope.top() == '+')
localSum += k;
else if (ope.top() == '-')
localSum -= k;
ope.pop();
}
}
if (s[i] == ' ')
continue;
if (s[i] == '(') { //计算新的子表达式结构,存储之前的和
num.push(localSum);
localSum = 0;
ope.push('(');
}
else if (s[i] == ')'){ //子表达式计算结束
int k = localSum;
localSum = num.top();
while (!ope.empty() && ope.top() == '(') //弹出所有出结果了的子表达式
ope.pop();
if (ope.empty() || ope.top() == '+')
localSum += k;
else if (ope.top() == '-')
localSum -= k;
if (!ope.empty()) //子表达式为总表达式
ope.pop();
num.pop();
}
else //压入操作符符号
ope.push(s[i]);
}
return localSum;
}
};
//无括号的计算器,使用双栈
class Solution{
stack num;
stack opt;
int compute(){
int p = num.top();num.pop();
int q = num.top();num.pop();
char c = opt.top();opt.pop();
if (c == '*')
return q * p;
else if (c == '/')
return q / p;
else if (c == '+')
return q + p;
return q - p;
}
public:
int calculate(string s){
int n = s.size(), i = 0, ans = 0;
unordered_map pri{{'*', 1}, {'/', 1}, {'+', 0}, {'-', 0}};
//pri['*']=1,pri['/']=1,pri['+']=0,pri['-']=0;
while (i < n){
if (s[i] == ' ')
i++;
else if (!isdigit(s[i])){
while (!opt.empty() && pri[s[i]] <= pri[opt.top()])
num.push(compute());
opt.push(s[i]);
i++;
}else{
int k = 0;
while (i < n && isdigit(s[i]))
k = k * 10 + (s[i++] - '0');
num.push(k);
}
}
while (!opt.empty())
num.push(compute());
return num.top();
}
};
//还可以用栈存储*和/的计算结果,后面再求总和
//一个队列作为压入队栈,一个作为弹出栈,队列当要弹出元素时,如果弹出栈为空,将压入栈的元素压入到弹出栈中。
class MyQueue {
public:
stack inStack, outStack;
void in2out(){
while (!inStack.empty()){
int x = inStack.top();
outStack.push(x);
inStack.pop();
}
}
/** Initialize your data structure here. */
MyQueue() {}
/** Push element x to the back of queue. */
void push(int x){
inStack.push(x);
}
/** Removes the element from in front of queue and returns that element. */
int pop(){
if (outStack.empty())
in2out();
int x = outStack.top();
outStack.pop();
return x;
}
/** Get the front element. */
int peek(){
if (outStack.empty())
in2out();
return outStack.top();
}
/** Returns whether the queue is empty. */
bool empty(){
return inStack.empty() && outStack.empty();
}
};
class Solution {
public:
int lengthOfLIS(vector &nums) {
int n = nums.size(), maxLen = 1;
vector dp(n, 1);
for (int i = 1; i < n; ++i) {
for (int j = 0; j < i; ++j) {
if (nums[i] > nums[j]) {
dp[i] = max(dp[i], dp[j] + 1);
maxLen = max(maxLen, dp[i]);
}
}
}
return maxLen;
}
};
//统计树最少未遍历的节点数cnt,遍历一个新节点,cnt--;遇到数字:cnt+=2;
class Solution {
public:
bool isValidSerialization(string preorder){
int n = preorder.size(), i = 0, cnt = 1;
while (i < n){
if (preorder[i] == ',')
i++;
else{
if (!cnt)
return false;
cnt--;
if (preorder[i] != '#')
cnt += 2;
while (i < n && preorder[i] != ',')
i++;
}
}
return cnt == 0;
}
};
//方法:放的方法为:根据大小,对于长和宽都满足要求的(都大于前一封),依次放;如果只有一条边满足要求,放最小的且满足要求的;
//问题:我们不应该放某条过大的边,因为这样会导致后面本来更多满足要求信封无法放入。所以对一条边排好序后,再对另一条边排序,为了放入更多的信封,我们可能会舍弃掉之前的信封。
class Solution{
public:
int maxEnvelopes(vector> &envelopes) {
sort(envelopes.begin(), envelopes.end(), [](const auto &e1, const auto &e2) {
return e1[0] < e2[0] || (e1[0] == e2[0] && e1[1] > e2[1]);
});
int n = envelopes.size();
/*//错误,没有考虑中间某个信封过大,导致后面本可以放进的信封无法放入。
auto pre=envelopes[0];
auto f=[](const auto& e1,const auto& e2){return e1[0]";
}
}
return cnt;
*/
//动态规划,用dp[i]存储当前可以放入i的最大信封数,即所有小于信封i的 信封k的位置的 值的最大值+1;
vector dp(n, 1);
for (int i = 0; i < n; ++i){
for (int j = 0; j < i; ++j){
if (envelopes[i][1] > envelopes[j][1] && dp[i] < dp[j] + 1) //信封j后能放下i,
dp[i] = dp[j] + 1;
}
}
return *max_element(dp.begin(), dp.end());
/*方法二:用f来存储信封应该在的位置,使得从0到该位置的信封满足要求,递增。0到当前的长度 表示 包含当前位置上的信封 所构成的最大信封数。
vector f = {envelopes[0][1]};
for (int i = 0; i < n; ++i){
int num = envelopes[i][1];
if (num > f.back())
f.push_back(num);
else{
auto it = lower_bound(f.begin(), f.end(), num);
*it = num;
}
}
return f.size();*/
}
};
struct cmp {
bool operator()(const int a, const int b) { return a % 3 > b % 3; }
};
class Solution {
public:
int strongPasswordChecker(string password) {
int n = password.size();
char pre;
vector rep;
int digit = -1, lower = -1, upper = -1, dup = 0;
for (int i = 0; i < n; ++i) {
if (i > 0 && pre == password[i])
dup++;
else {
if (dup > 2)
rep.push_back(dup);
dup = 1;
}
if (isdigit(password[i]))
digit++;
else if (password[i] >= 'A' && password[i] <= 'Z')
upper++;
else if (password[i] >= 'a' && password[i] <= 'z')
lower++;
pre = password[i];
}
int ans = 0;
if (dup > 2)
rep.push_back(dup);
if (upper > -1 && digit > -1 && lower > -1 && rep.empty() && n > 5 &&
n < 21)
return 0;
int off = 0, repcnt = 0, change = 0;
digit = digit > -1 ? 0 : 1;
lower = lower > -1 ? 0 : 1;
upper = upper > -1 ? 0 : 1;
change = digit + lower + upper;
for (int i = 0; i < rep.size(); ++i)
repcnt += rep[i] / 3;
if (n < 6) {
off = 6 - n;
while (off > 0 || change > 0 || repcnt > 0)
off--, change--, repcnt--, ans++;
} else if (n <= 20) {
ans = max(repcnt, change);
} else {
off = n - 20;
priority_queue, cmp> que(rep.begin(), rep.end());
while (off > 0 && !que.empty()) {
int x = que.top();
que.pop();
if (x / 3 - (x - 1) / 3)
repcnt--;
x--, off--, ans++;
if (x > 2)
que.push(x);
}
while (off > 0)
off--, ans++;
while (repcnt > 0 || change > 0)
change--, repcnt--, ans++;
}
return ans;
}
};
//方法:滑动窗口,要求的字符串是连续的,替换的字符是子串中出现次数较少的字符,且这些字符的总数不大于k。
//给定一个窗口,找里面字符出现最多的字符作为重复字符。窗口长度-其个数<=k,所以只有最大出现次数变大,窗口才可能变大。
//依次往窗口里添加字符,在满足要求的情况下,窗口不断增大,当不满足时,因为是连续的,去掉最左边。
int characterReplacement(char* s, int k)
{
int num[26]; //满足要求的区间内各字符的个数
memset(num, 0, sizeof(num));
int n = strlen(s);
int maxn = 0;
int left = 0, right = 0;
while (right < n) {
num[s[right] - 'A']++; //字符数加一
maxn = max(maxn, num[s[right] - 'A']); //区间内出现的最大字符个数
if (right - left + 1 - maxn > k) { //区间内需要替换的字符个数>最大替换次数,不满足要求,去掉最左边的字符。
num[s[left] - 'A']--; //则左边字符的个数减一
left++;
}
right++;
}
return right - left;
}
//方法:基本方法是申请一个大小为n的数组,用来标记下标为i的数是否出现。因为值全部小于n,所以可以利用原数组来表示,如果下标为i的数出现,则i位置上的数就加n。
//或者可以进行排序,排第i个位置时,往后查找为值为i的地址,并交互,则缺失的数会找不到。
int *findDisappearedNumbers(int *nums, int numsSize, int *returnSize)
{
for (int i = 0; i < numsSize; ++i)
nums[(nums[i] - 1) % numsSize] += numsSize;
int *ans = (int *)malloc(sizeof(int) * numsSize);
*returnSize = 0;
for (int i = 0; i < numsSize; ++i){
if (nums[i] <= numsSize)
ans[(*returnSize)++] = i + 1;
}
return ans;
}
//方法:构建两个堆,一个最大堆存储前半部分,一个最小堆存储后半部分,每次只需比较两个堆顶的值和新加入的值,并删掉窗口去掉的值,然后不断维护两个堆。
//窗口长度为奇数时,最大堆存储的个数比最小堆多一个,则大堆堆顶的值即为中位数。
//leetcode使用了优先队列和延迟删除,主要堆顶的元素不是需要删除的元素,则其作为中位数就不会出错,相当于两个队列将窗口同时往两边扩大。
#define MAXSIZE 100000
typedef struct node {
int i, v;
} node;
void maxHeapPush(node* heap, int* s, node k)
{
int son = ++(*s);
while (son / 2 > 0 && heap[son / 2].v < k.v) {
heap[son] = heap[son / 2];
son /= 2;
}
heap[son] = k;
}
void maxHeapPop(node* heap, int* s)
{
if (*s == 0)
return;
//node k=heap[1];
node v = heap[(*s)--];
int son = 2;
while (son <= *s) { //只需要与子节点中最大的比较,大于最大子节点,跳出,其他情况都需要交换。
if (son + 1 <= *s && heap[son + 1].v > heap[son].v)
son++;
if (v.v < heap[son].v)
heap[son / 2] = heap[son], son *= 2;
else
break;
}
heap[son / 2] = v;
}
void minHeapPush(node* heap, int* s, node k)
{
int son = ++(*s);
while (son / 2 > 0 && heap[son / 2].v > k.v) {
heap[son] = heap[son / 2];
son /= 2;
}
heap[son] = k;
}
void minHeapPop(node* heap, int* s)
{
if (*s == 0)
return;
//node k=heap[1];
node v = heap[(*s)--];
int son = 2;
while (son <= *s) {
if (son + 1 <= *s && heap[son + 1].v < heap[son].v)
son++;
if (v.v > heap[son].v)
heap[son / 2] = heap[son], son *= 2;
else
break;
}
heap[son / 2] = v;
}
void adjust(node* maxHeap, node* minHeap, int* maxCnt, int* minCnt, node k)
{
node x = maxHeap[1], y = minHeap[1];
if (*maxCnt <= *minCnt) {
if (*maxCnt == 0 || k.v <= y.v) //最开始时,大堆个数为0,小堆个数也为0,直接将元素添加到大堆中。当k=2且,大堆里的元素删除了,此时maxCnt==0,也可直接添加元素,因为中位数为两个堆顶元素的平均值。
maxHeapPush(maxHeap, maxCnt, k);
else {
minHeapPop(minHeap, minCnt);
minHeapPush(minHeap, minCnt, k);
maxHeapPush(maxHeap, maxCnt, y);
}
} else {
if (k.v >= x.v)//向小堆里添加元素时,大堆一定不为空,需要比较大堆顶和插入元素的大小
minHeapPush(minHeap, minCnt, k);
else {
maxHeapPop(maxHeap, maxCnt);
maxHeapPush(maxHeap, maxCnt, k);
minHeapPush(minHeap, minCnt, x);
}
}
}
int maxHeapDeletek(node* heap, int* s, node k)
{
int i = 1;
while (i <= *s && k.i != heap[i].i)
i++;
if (i > *s)
return 0;
node x = heap[(*s)--];
int son = i, p = son / 2;
if (p > 0 && x.v > heap[p].v) {
while (p > 0 && x.v > heap[p].v) {
heap[son] = heap[p];
son /= 2;
p = son / 2;
}
heap[son] = x;
} else {
son = i * 2;
while (son <= *s) {
if (son + 1 <= *s && heap[son + 1].v > heap[son].v)
son++;
if (heap[son].v > x.v)
heap[son / 2] = heap[son], son *= 2;
else
break;
}
heap[son / 2] = x;
}
return 1;
}
int minHeapDeletek(node* heap, int* s, node k)
{
int i = 1;
while (i <= *s && k.i != heap[i].i)
i++;
if (i > *s)
return 0;
node x = heap[(*s)--];
int son = i, p = son / 2;
if (p > 0 && x.v < heap[p].v) {
while (p > 0 && x.v < heap[p].v) {
heap[son] = heap[p];
son /= 2;
p = son / 2;
}
heap[son] = x; //不能用heap[p * 2] = x;因为可能p*2和 p/2之前的值不一样,
} else {
son = i * 2;
while (son <= *s) {
if (son + 1 <= *s && heap[son + 1].v < heap[son].v)
son++;
if (heap[son].v < x.v)
heap[son / 2] = heap[son], son *= 2;
else
break;
}
heap[son / 2] = x;
}
return 1;
}
// void print(node* heap, int cnt, char* s)
// {
// printf("%s:[", s);
// for (int i = 1; i <= cnt; ++i) {
// printf("(i=%d,v=%d),", heap[i].i, heap[i].v);
// printf("];");
// }
// }
// void minCheck(node* heap, int cnt)
// {
// for (int i = 1; i < cnt; ++i) {
// if (i * 2 <= cnt && heap[i].v > heap[i * 2].v) {
// printf("i=%d", i);
// break;
// }
// if (i * 2 + 1 <= cnt && heap[i].v > heap[i * 2 + 1].v) {
// printf("i=%d", i);
// break;
// }
// }
// }
double* medianSlidingWindow(int* nums, int numsSize, int k, int* returnSize)
{
*returnSize = numsSize - k + 1;
double* ans = (double*)malloc(sizeof(double) * (*returnSize));
node maxHeap[MAXSIZE], minHeap[MAXSIZE];
int minCnt = 0, maxCnt = 0, i = 0;
node n, m;
for (; i < k; ++i) { //由第一个窗口构建堆
n.v = nums[i], n.i = i;
adjust(maxHeap, minHeap, &maxCnt, &minCnt, n);
}
//print(maxHeap, maxCnt, "max");
//print(minHeap, minCnt, "min");
//maxCheck(maxHeap, maxCnt), minCheck(minHeap, minCnt);
for (int j = 0; i <= numsSize; ++i, ++j) {
node x = maxHeap[1], y = minHeap[1];
if (k % 2)
ans[j] = (double)x.v;
else
ans[j] = ((double)x.v + (double)y.v) / 2;//两个相加可能超过int的范围
if (i == numsSize) //判断是否继续移动窗口
break;
n.v = nums[i], n.i = i;
m.v = nums[j], m.i = j;
if (x.v == y.v) { //找到被窗口移除的元素,并从堆中删除
int f = maxHeapDeletek(maxHeap, &maxCnt, m);
if (f == 0)
minHeapDeletek(minHeap, &minCnt, m);
} else if (m.v <= x.v) {
maxHeapDeletek(maxHeap, &maxCnt, m);
} else {
minHeapDeletek(minHeap, &minCnt, m);
}
//maxCheck(maxHeap, maxCnt), minCheck(minHeap, minCnt);
// print(maxHeap,maxCnt,"max");
// print(minHeap,minCnt,"min");
adjust(maxHeap, minHeap, &maxCnt, &minCnt, n);
//maxCheck(maxHeap, maxCnt), minCheck(minHeap, minCnt);
// print(maxHeap,maxCnt,"max");
// print(minHeap,minCnt,"min");
}
return ans;
}
//方法:遍历,如果出现0则计数清零,否则加一
int findMaxConsecutiveOnes(int *nums, int numsSize){
int maxCnt = 0;
for (int i = 0; i < numsSize; ++i){
int cnt = 0;
while (i < numsSize && nums[i])
i++, cnt++;
if (maxCnt < cnt)
maxCnt = cnt;
}
return maxCnt;
}
//方法:用一个栈来存储未找到下一个更大元素的元素,则栈里的元素应该递减的;如果当前元素大于栈里的一些元素,则找到了这些元素的下一个更大元素,并将这些元素弹出。然后将当前元素加入栈;因为是循环查看的,所以遍历完数组后,应该再遍历一下,找栈里剩余元素的下一个更大元素。
class Solution{
public:
vector nextGreaterElements(vector &nums){
stack stk;
int n = nums.size();
vector ans(n, -1);
for (int i = 0; i < 2 * n - 1; ++i){
while (!stk.empty() && nums[stk.top()] < nums[i % n]){
ans[stk.top()] = nums[i % n];
stk.pop();
}
stk.push(i % n);
}
return ans;
}
};
//方法:要使最小值的和最大,则需最小值尽可能大,所以为每个最大值配第二大值构成一对,则和最大。
int cmp(const void *a, const void *b){
return *(int *)a - *(int *)b;
}
int arrayPairSum(int *nums, int numsSize){
qsort(nums, numsSize, sizeof(int), cmp);
int sum = 0;
for (int i = 0; i < numsSize; i = i + 2)
sum += nums[i];
return sum;
}
//将左半部分交换到右边,三种操作:1)直接交换得到回文数,2)左边减一,再交换得到的回文数;3)左边加一,再交换得到的回文数。
//这三种操作得到的回文数是最近的三个。分别对应的差值范围位abs(revleft-right),pow(10,n/2)-abs(revleft-right)
template outtype convert(intype val) { //使用字符串流进行类型转换
stringstream stream;
stream << val;
outtype out;
stream >> out;
return out;
}
class Solution {
public:
string nearestPalindromic(string n) {
int k = n.size();
int m = k / 2;
if (k == 1) {
k = convert(n) - 1; //1的答案是0,0的答案-1
return convert(k);
}
long base = pow(10, m);
string left_str = n.substr(0, (k + 1) / 2);
string right_str = n.substr(k - m, m);
long left_num = convert(left_str);
long right_num = convert(right_str);
//减一后再翻转
string left_minus_str = convert(left_num - 1);
long left_minus_num;
if (left_minus_str.size() < left_str.size() || left_num == 1) { //位数减少
left_minus_num = 1 + right_num;
left_minus_str = string(k - 1, '9');
} else {
string str = left_minus_str.substr(0, m);
reverse(str.begin(), str.end());
long rev = convert(str);
left_minus_num = base - rev + right_num;
left_minus_str += str;
}
//加一后再翻转
string left_plus_str = convert(left_num + 1);
long left_plus_num;
if (left_plus_str.size() > left_str.size()) { //位数增多
left_plus_num = base - right_num;
left_plus_str = "1";
left_plus_str += string(k - 1, '0');
left_plus_str += "1";
} else {
string str = left_plus_str.substr(0, m);
reverse(str.begin(), str.end());
long rev = convert(str);
left_plus_num = base - right_num + rev;
left_plus_str += str;
}
//直接翻转
string rev_left_str = n.substr(0, m);
reverse(rev_left_str.begin(), rev_left_str.end());
long left_rev_num = convert(rev_left_str);
left_rev_num = abs(right_num - left_rev_num);
rev_left_str = left_str + rev_left_str;
if (left_rev_num == 0) {
return left_minus_num > left_plus_num ? left_plus_str : left_minus_str;
} else if (left_rev_num < left_minus_num) {
return left_rev_num > left_plus_num ? left_plus_str : rev_left_str;
}
return left_minus_num > left_plus_num ? left_plus_str : left_minus_str;
}
};
int **matrixReshape(int **nums, int numsSize, int *numsColSize, int r, int c, int *returnSize, int **returnColumnSizes)
{//使用 matrixReshape(*nums[],numSize,numsColSize[],r,c,&returnSize,&*returnColumnSizes);后面两个因为要修改,所以需要再使用一层指针
if (numsColSize[0] * numsSize != r * c || r == numsSize){
*returnColumnSizes = numsColSize;
*returnSize = numsSize;
return nums;
}
int **ans = (int **)malloc(sizeof(int *) * r);
int i = 0, k = 0;
*returnColumnSizes = (int *)malloc(sizeof(int) * r); //加*表示修改其值。
while (i < r * c){
int j = 0;
int *col = (int *)malloc(sizeof(int) * c);
while (j < c){
col[j++] = nums[i / numsColSize[0]][i % numsColSize[0]];
i++;
}
ans[k] = col;
(*returnColumnSizes)[k++] = c; //*returnColumnSizes为一个数组地址,[]的优先级高于*
}
*returnSize = r;
return ans;
}
//方法:用双指针,统计s2两个指针中间的字符串各字符出现的次数是否等于s1中各字符的出现次数,且长度恰好等于s1的长度。
//另外可以用给定固定长度,判断字符出现次数是否满足要求。
bool checkInclusion(char *s1, char *s2)
{
int len1 = 0;
int cnt[26] = {0};
while (*s1) //统计s1字符串各字符出现的次数和字符串长度
cnt[*s1 - 'a']++, s1++, len1++;
int win = 0;
char *left = s2, *right = s2;
while (*right) {
int i = *right - 'a';
cnt[i]--, win++, right++; //右指针向右移动,耗费一个字符c,长度加一
while (cnt[i] < 0) //耗费c的字符数多于s1中的c字符的出现次数,左指针右移,直到将超出耗费的字符c补回。
cnt[*left - 'a']++, left++, win--;
if (win == len1)
return true;
}
return false;
}
//dp[i][j]存储的是前i个数字组成的数组,其逆序对为j的个数。则dp[i][j]=sum(dp[i-1][j-window+1:j]);其中window=i;表示窗口长度,因为数字i可以产生的新逆序对的范围为0到i-1;对应依次从右边逐步插入到左边。
class Solution {
const int mod = 1e9 + 7;
public:
int kInversePairs(int n, int k) {
if (k == 0)
return 1;
vector> dp(2, vector(k + 1, 0));
dp[1][0] = 1;
int row, preRow = 1;
for (int i = 2; i <= n; ++i) {
row = i & 1;
int window = i;
int window_sum = 0;
for (int j = 0; j <= k; ++j) {
window_sum += dp[preRow][j];
if (j >= window) {
window_sum -= dp[preRow][j - window];
}
window_sum = window_sum >= 0 ? window_sum % mod : window_sum + mod;
dp[row][j] = window_sum;
}
preRow = row;
}
return dp[row][k];
}
};
//因为长度确定,平均值最大,也即和最大,
double findMaxAverage(int* nums, int numsSize, int k)
{
int sum = 0, i;
for (i = 0; i < k; ++i) {
sum += nums[i];
}
int j = 0, maxSum = sum;
while (i < numsSize) {
int x = nums[i++] - nums[j++]; //和变化的值
if (x > 0 && x + sum > maxSum) { //最大和出现的情况
maxSum = x + sum;
}
sum += x; //更新和
}
return (double)maxSum / k;
}
//方法:每轮比较相连的三个数,nums[i-1],nums[i],nums[i+1];
//1.如果两端满足要求(nums[i-1]<=nums[i+1]),如果中间的数不满足要求,修改中间的数;
//2.如果两端不满足要求,前两个数满足要求,修改最后一个数;
//3.如果后两个数满足要求,修改第一个数(该情况只可能出现在数组最前面。数组前面放一个最小的数,则变为第一种情况);
//4.其他情况则至少修改两个数。一步一步移动,则该情况不会出现。
#define MIN -100000
bool checkPossibility(int *nums, int numsSize)
{
int pre = MIN, k = 0;
for (int i = 0; i < numsSize - 1; ++i){
if (pre <= nums[i + 1]){
if (nums[i] < pre || nums[i] > nums[i + 1]) //中间的数不满足要求
k++, nums[i] = pre;
}else if (nums[i] >= pre)//前两个数满足要求
k++, nums[i + 1] = nums[i];
else //其他情况不会出现
return false;
if (k == 2)
return false;
pre = nums[i];
}
return true;
}
//leetcode上的方法,当不满足非递减要求时,可以:1)将nums[i]修改为nums[i+1],或2)将nums[i+1]修改为nums[i];
//情况1,降低前面的值(i==0),用掉一次修改机会,继续看后边是否满足;
//情况2,提升后面的值(i>0),还需要看nums[i+2]和nums[i]的关系,如果提升后的值大于随后的值,则不满足要求
bool checkPossibility(int* nums, int numsSize) {
int cnt = 0;
for (int i = 0; i < numsSize - 1; ++i) {
int x = nums[i], y = nums[i + 1];
if (x > y) {
cnt++;
if (cnt > 1) {
return false;
}
if (i > 0 && y < nums[i - 1]) {
nums[i + 1] = x;
}
}
}
return true;
}
//需要一个哈希表来存储各元素出现的次数,要求该元素出现次数最多且长度最小的子串,则该子串两边恰好为该元素,所以可以再加两个哈希表,用来存储元素的起始下标和终止下标。
#define M 50000
int findShortestSubArray(int *nums, int numsSize){
int cnt[M], startIndex[M], endIndex[M];
for (int i = 0; i < M; ++i)
startIndex[i] = -1;
memset(cnt, 0, sizeof(int) * M);
int maxCnt = 0, minLen = M;
for (int i = 0; i < numsSize; ++i){
if (startIndex[nums[i]] == -1)
startIndex[nums[i]] = i;
endIndex[nums[i]] = i;
cnt[nums[i]]++;
int len = endIndex[nums[i]] - startIndex[nums[i]] + 1;
if (cnt[nums[i]] > maxCnt || (cnt[nums[i]] == maxCnt && minLen > len)){
minLen = len;
maxCnt = cnt[nums[i]];
}
}
return minLen;
}
//方法:使用一个最大堆和最小堆,大的数放在最小堆中,小的数放在最大堆里。
//大堆没有必要,因为构建好小堆后,如果新加的数大于堆顶,则将堆顶元素和替换为新元素,并调整。否则堆无需变化。
#define MAX 10001
typedef struct{
int minHeap[MAX], maxHeap[MAX];
int minSize, maxSize, k;
} KthLargest;
int less(int a, int b) { return a < b; }
int greater(int a, int b) { return a > b; }
void heapAdd(int *heap, int *size, int val, int (*cmp)(int, int)){//从堆末尾增加元素,并调整。
int son = ++*size;
while (son / 2 > 0 && cmp(val, heap[son / 2]))
heap[son] = heap[son / 2], son /= 2;
heap[son] = val;
}
void heapReplace(int *heap, int size, int *val, int (*cmp)(int, int)){ //出现较大的数,替换最小堆里最小的数
int k = heap[1];
int son = 2;
while (son <= size){
if (son + 1 <= size && cmp(heap[son + 1], heap[son]))
son += 1;
if (cmp(heap[son], *val))
heap[son / 2] = heap[son];
else
break;
son *= 2;
}
heap[son / 2] = *val;
*val = k;
}
KthLargest *kthLargestCreate(int k, int *nums, int numsSize){
KthLargest *heap = (KthLargest *)malloc(sizeof(KthLargest));
heap->minSize = 0, heap->maxSize = 0, heap->k = k;
int i;
for (i = 0; i < numsSize && i < k; ++i)//构建小堆
heapAdd(heap->minHeap, &heap->minSize, nums[i], less);
for (i; i < numsSize; ++i){//调整两个堆
if (nums[i] > heap->minHeap[1])
heapReplace(heap->minHeap, heap->minSize, &nums[i], less);
heapAdd(heap->maxHeap, &heap->maxSize, nums[i], greater);
}
return heap;
}
int kthLargestAdd(KthLargest *obj, int val){
if (obj->k > obj->minSize) //可能最开始通过的元素个数小于k。
heapAdd(obj->minHeap, &obj->minSize, val, less);
else{
if (obj->minHeap[1] < val)
heapReplace(obj->minHeap, obj->minSize, &val, less);
heapAdd(obj->maxHeap, &obj->maxSize, val, greater);
}
return obj->minHeap[1];
}
void kthLargestFree(KthLargest *obj){
free(obj);
}
//方法:总的和减去右边的和==左边的和。
int pivotIndex(int *nums, int numsSize)
{
int sum = 0, leftSum = 0;
for (int i = 0; i < numsSize; ++i)
sum += nums[i];
for (int i = 0; i < numsSize; ++i)
{
sum -= nums[i];
if (leftSum == sum)
return i;
leftSum += nums[i];
}
return -1;
}
//失败案例,
int pivotIndex(int *nums, int numsSize)
{
int *rsum = (int *)malloc(sizeof(int) * (numsSize + 1));
int *lsum = (int *)malloc(sizeof(int) * (numsSize + 1));
rsum[numsSize] = lsum[0] = 0;
for (int i = 0; i < numsSize; ++i) //从左往右求和,i左边的和
lsum[i + 1] = lsum[i] + nums[i];
for (int i = numsSize - 1; i >= 0; --i) //从右往左求和,i右边的和
rsum[i] = rsum[i + 1] + nums[i];
for (int i = 0; i < numsSize; ++i){
if (lsum[i] == rsum[i + 1]) {
free(rsum);
free(lsum);
return i;
}
}
free(rsum);
free(lsum);
return -1;
}
//回文串加子串,dp[i][j]存储i到j的回文子序列最大个数。在已知dp[i][j]中间的值后,我们可以根据s[i]的s[j]的关系,与i-j中间s[i]重复的个数推导出dp[i][j]的值
class Solution {
const int mod = 1e9 + 7;
public:
int countPalindromicSubsequences(string S) {
int n = S.size();
vector> dp(n, vector(n, 0));
for (int i = n - 1; i >= 0; --i) {
dp[i][i] = 1;
vector cnt(4, 0);
int id = S[i] - 'a';
for (int j = i + 1; j < n; ++j) {
if (S[i] == S[j]) {
if (cnt[id] == 0) {
dp[i][j] = 2 * dp[i + 1][j - 1] + 2;
} else if (cnt[id] == 1) {
dp[i][j] = 2 * dp[i + 1][j - 1] + 1;
} else {
int x = i + 1, y = j - 1;
while (S[x] != S[i])
x++;
while (S[y] != S[i])
y--;
dp[i][j] = 2 * dp[i + 1][j - 1] - dp[x + 1][y - 1];
}
} else {
dp[i][j] = dp[i][j - 1] - dp[i + 1][j - 1] + dp[i + 1][j];
}
dp[i][j] = (dp[i][j] >= 0) ? dp[i][j] % mod : dp[i][j] + mod;
//cout << dp[i][j] << ",";
cnt[S[j] - 'a']++;
}
}
return dp[0][n - 1];
}
};
//方法:所有情侣牵手成功,则必须要求在偶数位i与下一位奇数位i+1坐的是一对情侣。这样的位子对有rowsize/2对,如果某对位子上坐的不是一对情侣,只需交互其中一个即可。
//还可以使用并查集,连通分量
int minSwapsCouples(int *row, int rowSize){
int index[rowSize];
for (int i = 0; i < rowSize; ++i)
index[row[i]] = i;
int cnt = 0;
for (int i = 0; i < rowSize; i = i + 2){//下一对位子
if (abs(row[i] - row[i + 1]) != 1 || (row[i] + row[i + 1]) % 4 != 1){ //一对位子上坐的不是一对情侣
if (row[i] % 2){ //为靠左边的人找到对应的情侣,将右边的人替换到左边情侣的位子上去,修改右边人的位子下标
row[index[row[i] - 1]] = row[i + 1];
index[row[i + 1]] = index[row[i] - 1];
}else{ //同上,只是左边人的情侣取决于左边人的奇偶性;可以用异或运算
row[index[row[i] + 1]] = row[i + 1];
index[row[i + 1]] = index[row[i] + 1];
}
//row[index[row[i]^1]]=row[i+1];
//index[row[i+1]]=index[row[i]^1];
cnt++;
}
}
return cnt;
}
//方法:依次比较相邻两行(前一行0到n-1,后一行1到n)的元素即可。
bool isToeplitzMatrix(int **matrix, int matrixSize, int *matrixColSize){
for (int i = 1; i < matrixSize; ++i){
for (int j = 1; j < matrixColSize[i]; ++j){
if (matrix[i][j] - matrix[i - 1][j - 1])
return false;
}
}
return true;
}
//也可以直接在原数组上修改。
int **flipAndInvertImage(int **A, int ASize, int *AColSize, int *returnSize, int **returnColumnSizes){
*returnSize = ASize;
int **array = (int **)malloc(sizeof(int *) * ASize); //指针构成的数组
*returnColumnSizes = (int *)malloc(sizeof(int) * ASize); //指向数组的指针
for (int i = 0; i < ASize; ++i){
(*returnColumnSizes)[i] = AColSize[i]; //*returnColumnSizes获取其地址,
*(array + i) = (int *)malloc(sizeof(int) * AColSize[i]); //修改指针数组里的值
for (int j = 0; j < AColSize[i]; ++j)
array[i][j] = A[i][AColSize[i] - j - 1] ^ 1;
}
return array;
}
//找规律,先记录前序和,作为左边界,如果某个位置x1的前序和大于后面位置x2的前序和,x1可能成为左边界的话,则x2为更小的左边界。所以应该存储递增左边界。
//左右边界满足要求时,左边界可能不是最大值,无法使长度更小;则尝试右移左边界
class Solution {
public:
int shortestSubarray(vector& A, int K)
{
int n = A.size();
vector sum(n + 1);
for (int i = 0; i < n; i++) {
sum[i + 1] = sum[i] + A[i];
}
int minLen = n + 1, right = 0;
deque que;
while (right <= n) {
while (!que.empty() && sum[right] <= sum[que.back()])//相比当前值作为左边界,不可能成为左边界的值。
que.pop_back();
while (!que.empty() && sum[right] - sum[que.front()] >= K) {//找更靠右的左边界
minLen = fmin(minLen, right - que.front());
que.pop_front();
}
que.push_back(right);
right++;
}
return minLen > n ? -1 : minLen;
}
};
//和832. 翻转图像一样
int **transpose(int **matrix, int matrixSize, int *matrixColSize, int *returnSize, int **returnColumnSizes){
*returnSize = matrixColSize[0];
*returnColumnSizes = (int *)malloc(sizeof(int) * matrixColSize[0]);
int **array = (int **)malloc(sizeof(int *) * matrixColSize[0]);
for (int i = 0; i < matrixColSize[0]; ++i){
(*returnColumnSizes)[i] = matrixSize;
array[i] = (int *)malloc(sizeof(int) * matrixSize);
for (int j = 0; j < matrixSize; ++j)
array[i][j] = matrix[j][i];
}
return array;
}
//方法:是否存在两者总和之差==两个元素之差的2倍;其他方法可以以空间换时间,遍历A时,查找B中是否存在abs(Asum-Bsum)/2-A[i]的值
int* fairCandySwap(int* A, int ASize, int* B, int BSize, int* returnSize)
{
int ASum = 0, BSum = 0;
for (int i = 0; i < ASize; ++i)
ASum += A[i];
for (int i = 0; i < BSize; ++i)
BSum += B[i];
int k = ASum - BSum;
int* ans = (int*)malloc(sizeof(int) * 2);
for (int i = 0; i < ASize; ++i) {
for (int j = 0; j < BSize; ++j) {
if (2 * (A[i] - B[j]) == k) {
ans[0] = A[i], ans[1] = B[j];
*returnSize = 2;
return ans;
}
}
}
return ans;
}
//有规律,和乘积过程中不会产生进位。
typedef long long ll;
template T1 convert(T2 s) {
stringstream io;
io << s;
T1 out;
io >> out;
return out;
}
class Solution {
ll right_num, left_num;
string next_string(string &s) {
int n = s.size();
if (n == 1 && s != "9") {
int x = convert(s);
return convert(x + 1);
}
if (s == "9")
return string("11");
int m = (n + 1) / 2;
string left_str = s.substr(0, m);
int i = m;
while (i && left_str[i - 1] == '9') {
left_str[--i] = '0';
}
if (i)
left_str[i - 1] += 1;
else
left_str.insert(0, 1, '1');
string rev_str = left_str.substr(0, n / 2);
reverse(rev_str.begin(), rev_str.end());
return left_str + rev_str;
}
bool ispali(const string &s) {
string r_str=s;
reverse(r_str.begin(), r_str.end());
return s == r_str;
}
bool ispowpali(ll num) {
ll x = num * num;
if (x <= right_num && x >= left_num) {
string s = convert(x);
return ispali(s);
}
return false;
}
public:
int superpalindromesInRange(string left, string right) {
right_num = convert(right);
left_num = convert(left);
ll sqrt_left = sqrt(left_num);
left = convert(sqrt_left);
int n = left.size();
int m = n / 2;
if (n > 1) {
string l_str = left.substr(0, m);
string r_str = left.substr(n - m, m);
reverse(l_str.begin(), l_str.end());
ll l_num = convert(l_str);
if (l_num <= convert(r_str))
left = next_string(left);
else
left = left.substr(0, (n + 1) / 2) + l_str;
}
int cnt = 0;
sqrt_left = convert(left);
while (sqrt_left * sqrt_left <= right_num) {
if (ispowpali(sqrt_left))
cnt++, cout << sqrt_left << "," << sqrt_left * sqrt_left << ";";
left = next_string(left);
sqrt_left = convert(left);
}
return cnt;
}
};
//对每个子串找最小值,时间复杂度O(n^2),超时。依次获取每个字串和其最小值。
//leetcode方法:找出当前元素作为最小值的左边界(最小值不唯一)和右边界(唯一最小值)。该区间以当前元素为最小值的子串个数为(i-prev[i])*(next[i]-i)。
#define MOD 1000000007
int sumSubarrayMins(int *arr, int arrSize){
int prev[arrSize], next[arrSize];
int minQue[arrSize], right = 0;//存储当前位置之前的最小值和前一位置的元素(下标)
for (int i = 0; i < arrSize; ++i){ //小于当前元素的左边界
while (right > 0 && arr[i] <= arr[minQue[right - 1]])
right--;
if (right == 0)
prev[i] = -1;
else
prev[i] = minQue[right - 1];
minQue[right++] = i;
}
right = 0;
for (int i = arrSize - 1; i >= 0; --i){//小于等于当前元素的左边界
while (right > 0 && arr[i] < arr[minQue[right - 1]])
right--;
if (right == 0)
next[i] = arrSize;
else
next[i] = minQue[right - 1];
minQue[right++] = i;
}
int sum = 0;
for (int i = 0; i < arrSize; ++i){
sum += (long)arr[i] * (i - prev[i]) * (next[i] - i) % MOD;
sum %= MOD;
}
return sum;
}
//维护递增栈,
//新加一个元素j,左边界i依次往左运动,则产生了新的子串,对于不影响新子串中最小值的,其和不变(=之前统计的和-最小值改变了的部分的和),如果新加值为新串的最小值,需要统计其作为最小值的新子串的个数。
//aaaa; "" 1; "" a 2; "" a (a) aa 3-1; "" a aa (a) (aa) aaa 5-2; "" a aa aaa (a) (aa) (aaa) aaaa 8-3;
// a 1; a aa (a) 3-1; a aa (aa) aaa (a) 5-2; a aa aaa (aa) (aaa) aaaa (a) 7-3;
//lee; "" 1; "" l 2; "" l e le 4; "" l e le (e) (le) ee lee 7-2;
// l 1; l le e 3; l (le) (e) le lee ee e 7-2;
//每次新加一个字符,总个数变为2*n,重复的个数为上一次添加该元素前一位置的个数;
class Solution {
const int mod = 1e9 + 7;
public:
int distinctSubseqII(string S) {
int n = S.size();
vector dp(n+1), last(26, -1);
dp[0] = 1;
for (int i = 0; i < n; ++i) {
int id = S[i] - 'a';
dp[i+1] = dp[i] * 2 ;
if (last[id] >= 0)
dp[i+1] = dp[i+1] - dp[last[id]];
dp[i+1] = dp[i+1] >= 0 ? dp[i+1] % mod : dp[i+1] + mod;
last[id] = i;
//cout << dp[i+1] << ";";
}
dp[n]--;
if(dp[n]<0)dp[n]+=mod;
return dp[n];
}
};
//方法:判断arr[i]和arr[i-1]的大小连续反转的次数,计数终止情况:1.当出现等于时,需要向后移动直到出现不等于;2.反转未出现时。
//其他方法用双指针和动态规划。
int maxTurbulenceSize(int *arr, int arrSize)
{
if (arrSize < 2)
return arrSize;
int cnt = 1, i = 1, maxCnt = 1;
while (i < arrSize){
while (i < arrSize && arr[i] == arr[i - 1]) //找到不等于
++i;
if (i == arrSize)
break;
bool pre = arr[i] > arr[i - 1]; //前一个大小比较
cnt = 2, i++;
while (i < arrSize){
if (arr[i] == arr[i - 1]) //出现等于,终止
break;
bool cur = arr[i] > arr[i - 1]; //后一个大小比较
if ((pre || cur) && !(pre && cur)){//异或
i++, pre = cur, cnt++;
}
else
break;
}
if (cnt > maxCnt)
maxCnt = cnt;
}
return maxCnt;
}
//方法:统计窗口里各数字出现的次数cnt[A[i]],不同数字的个数num,如果num==k(条件,出现一个新的数),则找到一个合适窗口,然后从左往右缩小窗口,看看还有多少窗口满足num==k(终止条件为有一个数的出现次数变为0),只考虑包含新加入数字在内的子窗口。
int currentWindow(int *A, int *cnt, int left, int right) //当前窗口满足K==num,从左往右缩小窗口,有多少个窗口满足。
{
int ans = 0;
int i = left;
while (i < right){
if (--cnt[A[i++]])
ans++;
else
break;
}
for (int j = left; j < i; ++j) //复原出现频率。
cnt[A[j]]++;
return ans;
}
int subarraysWithKDistinct(int *A, int ASize, int K)
{
int cnt[ASize + 1];
memset(cnt, 0, sizeof(int) * (ASize + 1));
int right = 0, left = 0, num = 0, ans = 0;
while (right < ASize){
if (cnt[A[right]] == 0)
num++;
cnt[A[right]]++;
if (num == K){ //满足要求的新窗口,
ans++;
ans += currentWindow(A, cnt, left, right);//包含最右端数的子窗口满足要求的个数。
} else if (num > K) { //新窗口不满足要求,
while (left < right && --cnt[A[left++]]); //删掉一个数,使其出现次数为0
ans++, num--;
ans += currentWindow(A, cnt, left, right); //新满足的窗口
}
right++;
}
return ans;
}
//双指针:
//关系:给定一个窗口,我们可以先求当前窗口最多包含k个不同数的子窗口个数(=包含1到k个不同数的子窗口数之和),减去最多包含k-1个不同数的子窗口的个数,即得包含k个不同数的子窗口数。
//统计新加一个数,会导致多少个新子窗口出现?,答案是L+1,L为之前窗口的大小,从右往左依次选不同长度0-L的子窗口加上新加入的数构成新的子窗口。
//新加一个数,如果不同数的个数小于k,统计新的子窗口个数;
//如果不同个数大于K,则应该缩小之前窗口,使其不同数个数变为k-1,即将左指针右移,直到有一个数的统计次数变为0;
//方法:1.一个字串翻转两次,相当于没有翻转;2.各子串的翻转顺序对结果没有影响。遇到0就翻转接下来的K个数,则时间复杂度O(NK),超时。
int minKBitFlips(int *A, int ASize, int K){
int cnt = 0;
for (int i = 0; i < ASize; ++i){
if (A[i] == 0){
if (i + K <= ASize){
for (int j = 0; j < K; ++j)
A[i + j] ^= 1;
cnt++;
}else
return -1;
}
}
return cnt;
}
//统计每个元素翻转的次数,
class Solution {
public:
int minKBitFlips(vector<int> &A, int K){
int revCnt = 0, ans = 0;
int n = A.size();
vector<int> diff(n + 1, 0);
for (int i = 0; i < n; ++i){
revCnt += diff[i]; //得到当前的位置翻转的次数
if ((revCnt & 1) == A[i]){ //优先级
if (i + K > n)
return -1;
diff[i + K]--; //连续翻转k个,只影响第k和k+1处的翻转差。
revCnt++;
ans++;
}
}
return ans;
}
};
//方法:统计窗口内0出现的次数,如次数大于k,则左右边界一起移动,否则,只移动右边界。
//leetcode使用求和后的差得到窗口间0的个数。
int longestOnes(int *A, int ASize, int K){
int cnt[2] = {0, 0};
int right = 0, left = 0, maxLen = 0;
while (right < ASize){
cnt[A[right++]]++;
if (cnt[0] > K)
cnt[A[left++]]--;
}
return right - left;
}
//字符串哈希,快速查找
const long long mod = ((long long)1 << 55) + 1; //小了会冲突
const int base = 26;
class Solution {
//快速将长度为N的字符串转为整型。
int searchN(vector &nums, string &s, int N, int n) {
long long key = 0, pow = 1;
// unordered_map> hash;
unordered_set hash;
for (int i = 0; i < N; ++i) {
key = (key * base + nums[i]) % mod;
pow = (pow * base) % mod;
}
// hash[key].insert(s.substr(0,N));
hash.insert(key);
for (int i = 1; i <= n - N; ++i) {
key = (key * base - (nums[i - 1] * pow) % mod + mod) % mod;
key = (key + nums[i + N - 1]) % mod;
// if (hash.count(key)&&hash[key].count(s.substr(i,N)))
if (hash.count(key))
return i;
// hash[key].insert(s.substr(i,N));
hash.insert(key);
}
return -1;
}
public:
string longestDupSubstring(string s) {
cout << mod << endl;
int n = s.size();
int left = 1, right = n - 1;
vector arr(n);
for (int i = 0; i < n; ++i)
arr[i] = s[i] - 'a';
string ans;
//使用二分查找
while (left <= right) {
int mid = (left + right) / 2;
int start = searchN(arr, s, mid, n);
if (start == -1)
right = mid - 1;
else {
cout << start;
ans = s.substr(start, mid);
left = mid + 1;
}
}
return ans;
}
};
//string提供了类似栈的操作。栈,将stack用string替代
class Solution{
string dfs(stack &st){
if (st.empty())
return "";
char c = st.top();
st.pop();
return dfs(st) + c;
}
public:
string removeDuplicates(string S){
stack st;
for (int i = 0; i < S.size(); ++i){
if (st.size() && st.top() == S[i])
st.pop();
else
st.push(S[i]);
}
return dfs(st);
}
};
//方法,记录没有使用技巧时的客户满意数,和使用技巧获得的最大增值数。
int maxSatisfied(int *customers, int customersSize, int *grumpy, int grumpySize, int X){
int increase = 0, sum = 0, maxIncrease = 0;
for (int i = 0; i < customersSize; ++i){
if (grumpy[i]) //右边界使用技巧
increase += customers[i];
else
sum += customers[i]; //不使用技巧的满意数
if (i >= X && grumpy[i - X] == 1) //左边界移除技巧
increase -= customers[i - X];
maxIncrease = fmax(increase, maxIncrease);
//printf("i=%d,sum=%d,inc=%d,maxInc=%d\n", i, sum, increase, maxIncrease);
}
return sum + maxIncrease;
}
//行和列都多申请一行,sum[i][j]表示i-1,j-1的前缀和,后面处理区间时,可以不用讨论边界
class Solution {
public:
int numSubmatrixSumTarget(vector> &matrix, int target) {
int n = matrix.size(), m = matrix[0].size();
vector> sum(n + 1, vector(m + 1, 0));
for (int i = 0; i < n; ++i) { //求前缀和,
int lineSum = 0;
for (int j = 0; j < m; ++j) {
lineSum += matrix[i][j];
sum[i + 1][j + 1] = lineSum + sum[i][j + 1];
}
}
int ans = 0;
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j <= n; ++j) {
//存储i到j行 0-k列的和,后面某个值减去哈希表里的(也可以不减,相当于表初始一个0,1),即得后面子数组的和,
unordered_map cnt{{0, 1}};
for (int k = 1; k <= m; ++k) {
int x = sum[j][k] - sum[i][k];
int y = x - target;
if (cnt.count(y) != 0) {
ans += cnt[y];
}
cnt[x]++;
}
}
}
return ans;
}
};
//使用动态规划来记录s1的i位置和s2的j位置,的结果,如果当前相等,则长度+1,否则,遍历完某个子串,取其最大值
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
int n = text1.size(), m = text2.size();
vector> dp(n + 1, vector(m + 1, 0));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (text1[i] == text2[j]) {
dp[i + 1][j + 1] = fmax(dp[i + 1][j + 1], dp[i][j] + 1);
} else {
dp[i + 1][j + 1] = fmax(dp[i + 1][j + 1],
fmax(dp[i][j + 1], dp[i + 1][j]));
}
}
}
return dp[n][m];
}
};
class DinnerPlates {
int cap;
int not_full_left, have_right;
vector> plates;
vectorcnt;
void right_have_num() {//查找最靠右有元素的stack下标
while (have_right >= 0 && cnt[have_right] == 0) {
have_right--;
}
}
public:
DinnerPlates(int capacity)
: cap(capacity), not_full_left(0), have_right(-1) {
plates.emplace_back(vector(capacity,0));
cnt.push_back(0);
}
void push(int val) {
plates[not_full_left][cnt[not_full_left]++]=val;
if (have_right < not_full_left)
have_right = not_full_left;
while (not_full_left < plates.size() && cnt[not_full_left] == cap) //最左边元素未满的stack下标
not_full_left++;
if (not_full_left == plates.size()){
plates.emplace_back(vector(cap,0));
cnt.push_back(0);
}
}
int pop() {
if (have_right < 0)
return -1;
int x = plates[have_right][--cnt[have_right]];
right_have_num();
return x;
}
int popAtStack(int index) {
if (index >= plates.size() || cnt[index]==0)
return -1;
int x = plates[index][--cnt[index]];
if (not_full_left > index)//左边产生新的未满的stack
not_full_left = index;
if (cnt[index] == 0 && have_right == index) //右边产生新的空的stack
right_have_num();
return x;
}
};
//超时,对于每一个字谜,都遍历所有字。统计好所有字所包含的字符。然后每个字谜,只需进行查找。
#define M 7
#define S 26
int* findNumOfValidWords(char ** words, int wordsSize, char ** puzzles, int puzzlesSize, int* returnSize){
int *ans=(int*)malloc(sizeof(int)*puzzlesSize);
*returnSize=puzzlesSize;
int cnt[wordsSize][S+1];
memset(cnt,0,sizeof(cnt));
for(int i=0;i<wordsSize;++i){
char *p=words[i];
int m=0;
while(*p){
if(cnt[i][*p-'a']==0)cnt[i][*p-'a']=1,m++;
p++;
}
cnt[i][S]=m;
}
for(int i=0;i<puzzlesSize;++i){
int n=0,pCnt[S]={0};
for(int j=0;j<M;++j)pCnt[puzzles[i][j]-'a']=j+1;//统计字谜的字符
for(int j=0;j<wordsSize;++j){
if(cnt[j][S]>7)continue;
int k,firstChar=0;
for(k=0;k<S;++k){ //字是否可以作为谜底
if(cnt[j][k]){
if(pCnt[k]==0)break;
else if(pCnt[k]==1)firstChar=1;
}
}
if(k==S&&firstChar)n++;
}
ans[i]=n;
}
return ans;
}
//将字符串转化为数值,用来存储字符串包含的字符 结构
class Solution {
public:
vector<int> findNumOfValidWords(vector<string>& words, vector<string>& puzzles) {
unordered_map<int, int> cnt;
for (auto w : words){
int mask = 0;
for (auto c : w)
mask |= (1 << (c - 'a'));
if (__builtin_popcount(mask) <= 7)
cnt[mask]++;
}
vector<int> ans;
for (auto p : puzzles){
int k = 0;
/*//求子集第一中方法
for (int i = 0; i < (1 << 6); ++i){
int mask = 0;
for (int j = 0; j < 6; ++j){
if (i & (1 << j))
mask |= (1 << (p[j + 1] - 'a'));
}
mask |= (1 << (p[0] - 'a'));
if (cnt.count(mask))
k += cnt[mask];
}*/
int mask = 0;
for (int i = 1; i < 7; ++i)
mask |= (1 << (p[i] - 'a'));
int subset = mask;
do{
int s = subset | (1 << (p[0] - 'a'));
if (cnt.count(s))
k += cnt[s];
subset = (subset - 1) & mask;
} while (subset != mask); //会递减到0,然后两者相等。
ans.push_back(k);
}
return ans;
}
};
//或者将字符结构按字典数排序,作为哈希表的key
//方法:利用双指针,当差值小于剩余的maxCost后,右指针右移;大于,左指针右移,窗口只会越来越大,添加和删除元素后的窗口如果不满足要求,左右指针都会右移。
//类似424.替换后的最长重复字符
int equalSubstring(char* s, char* t, int maxCost)
{
int left = 0, right = 0;
while (*(s + right)) {
maxCost -= abs(*(s + right) - *(t + right)); //减去新加入字符的消耗
if (maxCost < 0) {
maxCost += abs(*(s + left) - *(t + left));//去掉最左边的消耗,左指针右移
left++;
}
right++;
}
return right - left;
}
//先求靠前的k个值和,然后去掉最左边的元素,依次加入尾部的数,直到前面靠前的k个全部去掉。
//两边k个元素的和=总和-中间窗口的和
int maxScore(int *cardPoints, int cardPointsSize, int k)
{
int sum = 0, i;
for (i = 0; i < cardPointsSize && i < k; ++i)
sum += cardPoints[i];
if (k == cardPointsSize)
return sum;
int left = i - 1, right = cardPointsSize - 1, maxSum = sum;
while (i > 0){
sum -= cardPoints[--i];
sum += cardPoints[right--];
if (sum > maxSum)
maxSum = sum;
}
return maxSum;
}
//动态规划,dp[i][j]表示两数组前i,j个元素的最大点积。
class Solution {
const int inf = -1e6;
public:
int maxDotProduct(vector &nums1, vector &nums2) {
int n = nums1.size(), m = nums2.size();
vector> dp(2, vector(m + 1, 0));
if (m > n)
return maxDotProduct(nums2, nums1);
// int dp[2][m+1];
for (int i = 0; i <= m; ++i)
dp[0][i] = inf;
dp[1][0] = inf;
int row, preRow = 0;
for (int i = 1; i <= n; ++i) {
row = i & 1;
for (int j = 1; j <= m; ++j) {
int Mut = nums1[i - 1] * nums2[j - 1];
dp[row][j] = max(Mut, Mut + dp[preRow][j - 1]);
int x = max(dp[row][j - 1], dp[preRow][j]);
dp[row][j] = max(dp[row][j], x);
}
preRow = row;
}
return dp[row][m];
}
};
//使用堆来存储最大值和最小值,超时
typedef struct{
int val, index;
} node;
int less(int a, int b){return a < b;}
int greater(int a,int b){return a>b;}
void heapPush(node *heap, int size, node k, int (*cmp)(int, int)){
int son = size;
while (son / 2 > 0){
if (cmp(k.val, heap[son / 2].val))
heap[son] = heap[son / 2];
else
break;
son /= 2;
}
heap[son] = k;
}
void heapDelete(node *heap, int *size, int index, int (*cmp)(int, int)){
int i = 1;
while (i <= *size && heap[i].index != index)
++i;
if (i == *size){
(*size)--;
return;
}
node k = heap[(*size)--];
if (i / 2 > 0 && cmp(k.val, heap[i / 2].val)){
heapPush(heap, *size, k, cmp);
}else{
int son = i * 2;
while (son <= *size){
if (son + 1 <= *size && cmp(heap[son + 1].val, heap[son].val))
son++;
if (cmp(heap[son].val, k.val))
heap[son / 2] = heap[son];
else
break;
son *= 2;
}
heap[son / 2] = k;
}
}
void print(node *heap,int size){
for(int i=1;i<=size;++i)printf("%d(%d)>",heap[i].val,heap[i].index);
printf("\n");
}
int longestSubarray(int *nums, int numsSize, int limit)
{
if (numsSize < 2)
return 1;
int left = 0, right = 0, maxLen = 0, maxSize = 0, minSize = 0;
node maxHeap[numsSize+1], minHeap[numsSize+1];
while (right < numsSize){
node k = {nums[right], right};
maxSize++, minSize++;
heapPush(minHeap, minSize, k, less);
heapPush(maxHeap, maxSize, k, greater);
//print(maxHeap, maxSize), print(minHeap, minSize);
while (maxHeap[1].val - minHeap[1].val > limit){
heapDelete(minHeap, &minSize, left, less);
heapDelete(maxHeap, &maxSize, left, greater);
//print(maxHeap, maxSize), print(minHeap, minSize);
left++;
}
right++;
int len = right - left;
if (len > maxLen)
maxLen = len;
}
return maxLen;
}
//leetcode的方法:两个队列,分别存储到当前位置的窗口中可能成为最大值和最小值的元素。两个队列的头表示当前窗口内的最大值和最小值,当窗口内的最大值与最小值差不满足要求时,窗口左边界往右移动,直到删掉最大值或最小值。使得新窗口满足要求。
int longestSubarray(int *nums, int numsSize, int limit){
int maxQue[numsSize], minQue[numsSize];
int maxRight = 0, maxLeft = 0;
int minRight = 0, minLeft = 0;
int right = 0, left = 0;
int maxLen = 0;
while (right < numsSize){
//新加入一个元素后,删除那些到当前位置的窗口中不可能成为最大值(最小值)的元素
while (minLeft < minRight && minQue[minRight - 1] > nums[right])
minRight--;
while (maxLeft < maxRight && maxQue[maxRight - 1] < nums[right])
maxRight--;
//新加入的可能成为最大值(最小值)的元素
maxQue[maxRight++] = nums[right];
minQue[minRight++] = nums[right];
//到当前位置的窗口内,最大值与最小值的差大于limit,不满足要求,移动左边界,直到删除最大值或最小值,使窗口满足要求。
while (minLeft < minRight && maxLeft < maxRight && maxQue[maxLeft] - minQue[minLeft] > limit){
if (nums[left] == minQue[minLeft])
minLeft++;
if (nums[left] == maxQue[maxLeft])
maxLeft++;
left++;
}
right++;
maxLen = fmax(maxLen, right - left);
}
return maxLen;
}
//求角度atan2更方便,求区间和的最大值,可以将区间的角度整体加360后附在后面,就不用分开处理了。
class Solution {
public:
int visiblePoints(vector> &points, int angle, vector &location) {
int x = location[0], y = location[1];
int loc = 0, vertical = 0;
map deg;
for (auto p : points) { //求各点对应的角度,
int u = p[0], v = p[1];
if (x == u && y == v) {
loc++;
} else if (x == u) {
y < v ? deg[90]++ : deg[270]++;
} else if (y == v) {
x < u ? deg[0]++ : deg[180]++;
} else {
double t = fabs((double)(y - v) / (x - u));
double d = atan(t) * 180.0 / M_PI;
if (u > x) {
if (v < y)
d = 360.0 - d;
} else {
if (v > y)
d = 180.0 - d;
else
d = d + 180.0;
}
deg[d]++;
}
}
map::iterator start = deg.begin(), rstart;
double end = start->first + angle, rst;
int cnt = 0, maxp = 0;
while (start != deg.end() && start->first <= end) {
cnt += start->second;
start++;
}
if (start == deg.end())
return cnt + loc;
rstart = deg.begin(), maxp = cnt;
while (start != deg.end()) { //窗口整体右移
cnt += start->second;
rst = start->first - angle;
while (rstart->first < rst) {
cnt -= rstart->second;
rstart++;
}
maxp = max(maxp, cnt);
start++;
}
rstart++;
while (rstart != deg.end()) {//窗口分为前半部分,后半部分
end = rstart->first + angle;
start = deg.begin();
rst = end - 360;
cnt -= rstart->second;
while (start->first <= rst) {
cnt += start->second;
start++;
}
maxp = max(maxp, cnt);
rstart++;
}
return maxp+loc;
}
};
//整个数组的乘法和加法操作,超时
//使用线段数组或树状数组
const int mod = 1e9 + 7;
class SegmentTree
{
vector lazya, lazyb; //记录数据的操作,a*x+b,遍历到该点时才更新
public:
SegmentTree(int n) : lazya(n * 4, 1), lazyb(n * 4, 0) {}
void updateTree(int treeId, int left, int right, int i, int j, int a, int b) {
if (rightj) { //当前区间在操作区间外
return;
}
if (left >= i && right <= j) { //操作区间内的一个子空间
lazya[treeId] = ((long)lazya[treeId] * a) % mod;
lazyb[treeId] = ((long)lazyb[treeId] * a % mod + b) % mod;
return;
}
int lchild = 2 * treeId + 1, rchild = 2 * treeId + 2;
int mid = (left + right) / 2;
if (lazya[treeId] != 1 || lazyb[treeId] != 0) { //累积左右孩子上的a,b
lazya[lchild] = ((long)lazya[lchild] * lazya[treeId]) % mod;
lazyb[lchild] = ((long)lazyb[lchild] * lazya[treeId] % mod + lazyb[treeId]) % mod;
lazya[rchild] = ((long)lazya[rchild] * lazya[treeId]) % mod;
lazyb[rchild] = ((long)lazyb[rchild] * lazya[treeId] % mod + lazyb[treeId]) % mod;
}
lazya[treeId] = 1; lazyb[treeId] = 0;//消除父亲节点上的a,b
updateTree(lchild, left, mid, i, j, a, b);
updateTree(rchild, mid + 1, right, i, j, a, b);
}
int queryTree(int treeId, int left, int right, int i) {
if (left == right)return lazyb[treeId]; //查询到i节点
int lchild = 2 * treeId + 1, rchild = 2 * treeId + 2;
int mid = (left + right) / 2;
if (lazya[treeId] != 1 || lazyb[treeId] != 0) {
lazya[lchild] = ((long)lazya[lchild] * lazya[treeId]) % mod;
lazyb[lchild] = ((long)lazyb[lchild] * lazya[treeId] % mod + lazyb[treeId]) % mod;
lazya[rchild] = ((long)lazya[rchild] * lazya[treeId]) % mod;
lazyb[rchild] = ((long)lazyb[rchild] * lazya[treeId] % mod + lazyb[treeId]) % mod;
}
lazya[treeId] = 1; lazyb[treeId] = 0;
if (i <= mid)return queryTree(lchild, left, mid, i); //查询节点在当前空间的左半边
return queryTree(rchild, mid + 1, right, i); //右半边
}
};
const int M = 1e5;
class Fancy {
vectorarr;
SegmentTree tree;
int size;
public:
Fancy() :size(0), tree(M + 1) {}
void append(int val) {
tree.updateTree(0, 0, M, size, size, 1, val);
size++;
}
void addAll(int inc) {
tree.updateTree(0, 0, M, 0, size - 1, 1, inc);
}
void multAll(int m) {
tree.updateTree(0, 0, M, 0, size - 1, m, 0);
}
int getIndex(int idx) {
if (idx >= size || idx < 0)return -1;
return tree.queryTree(0, 0, M, idx);
}
};
思路
二分法;给定一个值当作最小值,看看是否可以连通左上角和右下角(深度搜索或广度搜索)。或者BFS+DP;BFS遍历图,并储存起始节点到当前节点的最小高度,
并查集;根据边的高度对边递增排序,然后依次合并边连接两个集合,并判断左上角和右下角是否在一个集合里。为每个节点编号,则表示一条边只需两个值。
dijkstra:查找队列里的最小值,则起始点到该点的最小距离可以确定下来,
#define MAXSIZE 10001
#define MAXHEIGHT 1000000
typedef struct node {
int k, v;
} node;
int dir[4][2] = { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 } };
void minHeapPop(node* queue, int s, int i)//末尾节点代替根节点
{
queue[0] = queue[i];
int son = i * 2;
while (son <= s) {
if (son + 1 <= s && queue[son + 1].v < queue[son].v)
son++;
if (queue[0].v > queue[son].v) { //插入节点还是比最小子节点大,则最小子节点上移
queue[son / 2] = queue[son];
son *= 2;
} else //比子节点都小。
break;
}
queue[son / 2] = queue[0];
}
void minHeapPush(node* queue, int s) //在末尾插入节点
{
int son = s;
queue[0] = queue[s];
while (son / 2 > 0 && queue[0].v < queue[son / 2].v) {//父亲节点大于子节点
queue[son] = queue[son / 2];//父亲节点下移
son /= 2;
}
queue[son] = queue[0];
}
int minimumEffortPath(int** heights, int heightsSize, int* heightsColSize)
{
int row = heightsSize, col = heightsColSize[0];
node queue[MAXSIZE];
int N = row * col, cnt = 0;
int visited[N], dis[N];
memset(visited, 0, sizeof(visited));
for (int i = 0; i < N; ++i)
dis[i] = MAXHEIGHT;
dis[0] = 0;
queue[++cnt].k = 0, queue[cnt].v = 0;
while (cnt >= 1) {
int x = queue[1].k;
queue[1] = queue[cnt--];
minHeapPop(queue, cnt, 1);
if (visited[x]) //已找到最小的边。
continue;
if (x == N - 1)
break;
visited[x] = 1;
int c = x % col, r = x / col;
for (int i = 0; i < 4; ++i) {
int dc = c + dir[i][1], dr = r + dir[i][0];
if (dr >= 0 && dr < row && dc >= 0 && dc < col ) {
int dx = dr * col + dc;
int h = abs(heights[dr][dc] - heights[r][c]);
int m = dis[x] > h ? dis[x] : h;
if (dis[dx] > m) {
dis[dx] = m;
queue[++cnt].k = dx, queue[cnt].v = dis[dx];
minHeapPush(queue, cnt);
}
}
}
// printf("push:");
// for(int a=1;a<=cnt;++a)printf("%d(%d)->",queue[a].k,queue[a].v);
// printf("\n");
}
return dis[N - 1];
}
//超时,时间为3^(m*n)
//方法:随便先连通一条路径,然后修改路径中最大高度的前一位置的方向,看看能否找到更小的路径
#define MAXHEIGHT 10e6
#define MAXCOLSIZE 101
#define min(a, b) (a > b ? b : a)
#define max(a, b) (a > b ? a : b)
void print(int **heights, int row, int col)
{
system("cls");
for (int i = 0; i <= row; ++i){
for (int j = 0; j <= col; ++j){
if (heights[i][j])
printf("%1d", heights[i][j] % 10);
else
printf(" ");
}
printf("\n");
}
Sleep(500);
}
// priorMax 从起始点沿着路径到当前节点的高度;
int gonext(int r, int c, int row, int col, int **heights, int priorMax, int *globle)
{
print(heights, row, col);
if (r == row && c == col){
*globle = priorMax;
return 0;
}
int t = heights[r][c];
heights[r][c] = 0;
int nextMin = MAXHEIGHT;
for (int k = 0; k < 4; ++k){
if (*globle == priorMax){ //返回到最大高度的前一位置
print(heights,row,col);
heights[r][c] = t;
return nextMin;
}
int flag = 0;
// 往下、往右、往上、往左
if (k == 0 && ++r <= row || k == 1 && ++c <= col ||
k == 2 && --r >= 0 && c < col && c > 0 ||
k == 3 && --c >= 0 && r < row && r > 0)
flag = 1;
if (flag && heights[r][c]){ //下一节点可达
int i = abs(t - heights[r][c]); //当前节点到下一节点的高度
if (i < *globle){ //可能存在更小高度的路径。
int m = max(priorMax, i);
int j = gonext(r, c, row, col, heights, m, globle); //下一节点到终点的最小高度
m = max(i, j);
nextMin = min(m, nextMin);
}
}
if (k == 0 && --r || k == 1 && --c || k == 2 && ++r || k == 3 && ++c);
}
heights[r][c] = t;
return nextMin;
}
int minimumEffortPath(int **heights, int heightsSize, int *heightsColSize)
{
int minColSize = MAXCOLSIZE;
for (int i = 0; i < heightsSize; ++i){
if (minColSize > heightsColSize[i])
minColSize = heightsColSize[i];
}
if (heightsSize == 1 && minColSize == 1)
return 0;
int globle = MAXHEIGHT;
gonext(0, 0, heightsSize - 1, minColSize - 1, heights, 0, &globle);
return globle;
}
//转化为循环
char x[5][3] = { "*", "||", "->", "||", "<-" };
int dirs[4][2] = { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 } }; //下,右,上,左
void print(int* arr, int row, int col)
{
system("cls");
for (int i = 0; i < col; ++i)
printf("%2d", i);
printf("\n");
for (int i = 0; i < row; ++i) {
printf("%2d ", i);
for (int j = 0; j < col; ++j) {
int k = i * col + j;
if (arr[k])
printf("%s", x[arr[k]]);
else
printf(" ");
}
printf("\n");
}
// Sleep(100);
}
void HideCursor()
{
CONSOLE_CURSOR_INFO cursor_info = {1, 0};
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
int minimumEffortPath(int **heights, int heightsSize, int *heightsColSize)
{
int row = heightsSize;
int col = heightsColSize[0];
if (row == 1 && col == 1)
return 0;
int globle = MAXHEIGHT;
int q[row * col][2];
int cnt = 0;
q[cnt][0] = 0, q[cnt][1] = 0;
int seen[row * col], priorMax[row * col];
memset(seen, 0, sizeof(seen));
memset(priorMax, 0, sizeof(priorMax));
seen[0] = 1;
int x, y;
while (cnt >= 0){
int k = 0;
while (k < 4){ //依次访问四个方向
x = q[cnt][0], y = q[cnt][1];
int nx = x + dirs[k][0];
int ny = y + dirs[k][1];
if ((k == 0 && nx < row ||
k == 1 && ny < col ||
k == 2 && nx >= 0 && ny < col - 1 && ny > 0 ||
k == 3 && ny >= 0 && nx < row - 1 && nx > 0) &&
!seen[nx * col + ny]){
int t = abs(heights[nx][ny] - heights[x][y]);
if (t < globle){
cnt++;
priorMax[cnt] = priorMax[cnt - 1] > t ? priorMax[cnt - 1] : t;
q[cnt][0] = nx, q[cnt][1] = ny;
printf("(%d,%d)[%d-%d]->", nx, ny, t, globle);
seen[nx * col + ny] = k + 1;
break;
}
}
k++;
if (x == row - 1 && y == col - 1) //到达终点
globle = priorMax[cnt];
while (cnt >= 0 && (k == 4 || globle == priorMax[cnt])){ //方向访问完,或最大值出现在前面,回退一步
//print(seen, row, col);
seen[q[cnt][0] * col + q[cnt][1]] = 0;
cnt--;
if (cnt < 0)
break;
x = q[cnt][0], y = q[cnt][1];
if (q[cnt + 1][0] > x){ //以前往下,现在往右
k = 1;
}else if (q[cnt + 1][1] > y){ //以前往右,现在往上
k = 2;
}else if (q[cnt + 1][0] < x){ //以前往上,现在往左
k = 3;
}
}
}
}
return globle;
}
//方法:哈希,存储节点可以连接的节点,中间节点为2个,两端节点为1个,从其中一个端点出发,遍历一遍即得数组。
#define M 100000
int* restoreArray(int** adjacentPairs, int adjacentPairsSize, int* adjacentPairsColSize, int* returnSize)
{
*returnSize = adjacentPairsSize + 1;
int to[2 * M + 1][2]; //在本地笔记本上出错,放在函数外面可以通过。
int cnt[2 * M + 1];
memset(cnt, 0, sizeof(cnt));
for (int i = 0; i < adjacentPairsSize; ++i) {
int x = adjacentPairs[i][0] + M, y = adjacentPairs[i][1] + M;
to[x][cnt[x]++] = y;
to[y][cnt[y]++] = x;
}
int* arr = (int*)malloc(sizeof(int) * (adjacentPairsSize + 1));
int k = 0, i;
for (i = 0; i < M * 2 + 1; ++i) {
if (cnt[i] == 1) {
break;
}
}
memset(cnt,0,sizeof(cnt)); //用来表示节点是否访问过
while (k <= adjacentPairsSize) {
arr[k++] = i - M;
cnt[i]=1;
int j=to[i][0];
if(cnt[j])i=to[i][1];
else i=j;
}
return arr;
}
//方法:不需要每个数字都求一下各位数之和,其和与前一位数字k-1、k/10有关。可以减少一些操作。
int sum(int k)
{
int s = 0;
while (k) {
s += (k % 10);
k /= 10;
}
return s;
}
int countBalls(int lowLimit, int highLimit)
{
int cnt[46], i = lowLimit, s;
memset(cnt, 0, sizeof(cnt));
while (lowLimit <= highLimit) {
s = sum(lowLimit);
cnt[s]++;
int k = (lowLimit / 10 + 1) * 10; //找到下一次发生进位的地方,即不满足数字加一,和加一规则的位置。
if (k > highLimit)
k = highLimit;
while (++lowLimit < k) {
cnt[++s]++;
}
}
int m = 0;
for (int i = 1; i < 46; ++i) {
if (m < cnt[i])
m = cnt[i];
}
return m;
}
//简单的遍历,找最小值。
class Solution {
public:
int nearestValidPoint(int x, int y, vector>& points) {
int minIndex = -1, minDis = 10000;
for (int i = 0; i < points.size(); ++i) {
if (x == points[i][0] || y == points[i][1]){
int dis = abs(x - points[i][0]) + abs(y - points[i][1]);
if (dis < minDis)
minDis = dis, minIndex = i;
}
}
return minIndex;
}
};
//判断一个数是否可以表示为3的幂之和,依次整除3,如果不能整除3,则可能有0次幂,减一,再整除,如果不行则为false.
class Solution {
public:
bool checkPowersOfThree(int n) {
while (n){
if (n % 3==2)
return false;
n /= 3;
}
return true;
}
};
//两节点的边数和,大于给定值,求这样的点对数。将遍历节点转为遍历边。
class Solution {
public:
vector countPairs(int n, vector> &edges, vector &queries) {
unordered_map overlap;
vector deg(n + 1, 0);
vector> distinctedges;
auto encode = [n](int v, int u) -> int { //将节点对编码为整数
return max(v, u) * n + min(v, u);
};
for (auto e : edges) {
int v = e[0], u = e[1];
int id = encode(v, u);
deg[v]++, deg[u]++;
if (overlap.find(id) == overlap.end()) {
overlap[id] = 1;
distinctedges.emplace_back(u, v);
} else
overlap[id]++;
}
vector sortDeg(deg.begin() + 1, deg.end());
sort(sortDeg.begin(), sortDeg.end());
vector ans;
for (auto x : queries) {
int l = 0, r = n - 1, cnt = 0;
while (l < n) { //查询两点的边之和大于query的节点对数,排序好的,从左往右遍历,r右边表示能和满足要求O(n)
while (r > l && sortDeg[l] + sortDeg[r] > x)
r--;
cnt += n - max(r, l) - 1;
l++;
}
for (auto e : distinctedges) {
int v = e.first, u = e.second;
int id = encode(v, u);
if (deg[v] + deg[u] > x && deg[v] + deg[u] - overlap[id] <= x)
cnt--;
}
ans.push_back(cnt);
}
return ans;
}
};
//检查字符串是否出现多个的由‘1’构成的子串。
class Solution {
public:
bool checkOnesSegment(string s) {
int i = 1, n = s.size();
while (i < n && s[i] == '1')
i++;
if (i == n)
return true;
while (i < n && s[i] == '0')
i++;
if (i == n)
return true;
return false;
}
};
//求还需多少各不大于limit的数,使得数组的和达到target。
class Solution{
public:
int minElements(vector &nums, int limit, int goal){
long sum = 0; //注意可能会超出int的范围
for (int i = 0; i < nums.size(); ++i)
sum += nums[i];
sum = abs(goal - sum);
return sum / limit + (sum % limit ? 1 : 0);
}
};
//给定一组分子分母,操作分子分母同时加一,给定n次操作,求最大的平均值
class cmp {
public:
bool operator()(const pair &p1, const pair &p2) {
double x = (double)(p1.first + 1) / (p1.second + 1) -
(double)(p1.first) / (p1.second);
double y = (double)(p2.first + 1) / (p2.second + 1) -
(double)(p2.first) / (p2.second);
return x < y;
}
};
//pair有预定义的大小于重载运算符,先比较第一个,再比较第二个;默认的less不会调用我们编写全局重载运算符
/*bool operator<(const pair &p1, const pair &p2) {
double x = (double)(p1.first + 1) / (p1.second + 1) -
(double)(p1.first) / (p1.second);
double y = (double)(p2.first + 1) / (p2.second + 1) -
(double)(p2.first) / (p2.second);
return x < y;
}*/
class Solution {
public:
double maxAverageRatio(vector> &classes, int extraStudents) {
priority_queue, vector>, cmp> heap;
int cnt = 0, n = classes.size();
for (auto c : classes) {
if (c[0] == c[1]) {
cnt++;
continue;
}
heap.emplace(c[0], c[1]);
}
while (extraStudents) {
if (!heap.empty()) { //可能为空
auto x = heap.top();
heap.pop();
heap.emplace(x.first + 1, x.second + 1);
}
extraStudents--;
}
double sum = cnt;
while (!heap.empty()) {
auto x = heap.top();
// cout<
//第一中方法超时
class Solution1 {
int gcd(int a, int b) {
if (a > b)return gcd(b, a);
while (b % a) {
int c = b % a;
b = a, a = c;
}
return a;
}
int gcd2(int a, int b) {
if (a > b)return gcd2(b, a);
while (a != b) {
int x = b - a;
if (x > a)b = x;
else b = a, a = x;
}
return a;
}
public:
int splitArray(vector& nums) {
int n = nums.size();
vector dp(n, 1);
for (int i = 1; i < n; ++i) {
dp[i] = dp[i - 1] + 1; //最多i单独为一个子数组
if (gcd(nums[i], nums[0]) > 1) {
dp[i] = 1;
continue;
}
for (int j = 1; j < i; j++) { //遍历前半部分,是否可以与nums[i]组成新的子数组
if (gcd(nums[i], nums[j]) > 1) {
dp[i] = min(dp[i], dp[j]);
}
}
}
return dp[n - 1];
}
};
const int M = 1e6 + 1;
const int N = 1e5 + 1;
int min_prime[M], prime[N];
int g[M];
class Solution {
public:
int splitArray(vector& nums) {
int n = nums.size();
int m = *max_element(nums.begin(), nums.end());
for (int i = 2; i <= m; i++) { //求2-m间的质数和每个数的最小质数因子
if (!min_prime[i]) { //质数的最小质数因子,不会在自己出现前出现
prime[++prime[0]] = i;
min_prime[i] = i;
}
for (int j = 1; j <= prime[0]; j++) { //遍历已经找到了的质数,求以他们为最小质数因子的数
if (i > m / prime[j]) break; //i*prime[j]大于数组里的最大值,
min_prime[i * prime[j]] = prime[j]; //向上求某个的数的最小值数
if (i % prime[j] == 0) break; //4*3;6*2;确保prime[j]是某个数可分的最小质数因子,减少重复计算
}
}
for (int j = 1; j <= prime[0]; j++) //所有结果设为最大值
g[prime[j]] = n;
for (int x = nums[0]; x > 1; x /= min_prime[x]) //初始化第一个节点,表示节点之前所需的最小分割次数
g[min_prime[x]] = 0;
int ans = 1;
for (int i = 0; i < n; i++) {
ans = i + 1; //新加元素可能导致的最大值
for (int x = nums[i]; x > 1; x /= min_prime[x]) //找到之前存在共同因子的数,他之前所需的最小分割次数+1
ans = min(g[min_prime[x]] + 1, ans);
if (i == n - 1) break;
for (int x = nums[i + 1]; x > 1; x /= min_prime[x]) //新加一个数,更新g,新加的数与之前的数存在共同因子,
g[min_prime[x]] = min(g[min_prime[x]], ans);
}
return ans;
}
};
//两个数组的交集
class Solution {
public:
vector computeSimilarities(vector>& docs) {
unordered_map>cnt;
int n = docs.size();
vectorans;
for (int i = 0; i < n; ++i) {
unordered_mapinter;
int m = docs[i].size();
for (auto c : docs[i]) {
if (i && cnt.find(c) != cnt.end()) {
for (auto x : cnt[c]) {
inter[x]++;
}
}
cnt[c].push_back(i);
}
char temp[100];
for (auto x : inter) {
ostringstream ostr;
ostr << x.first << "," << i << ": ";
ostr << setiosflags(ios::fixed) << setprecision(4);
ostr << (double)x.second / (docs[x.first].size() + m - x.second) + 1e-9; //1e-9需要,
ans.push_back(ostr.str());
// int id=x.first;
// double y=(double)x.second/(docs[id].size()+m-x.second);
// sprintf(temp, "%d,%d: %0.4lf", id, i, y + 1e-9);
// ans.push_back(temp);
}
}
return ans;
}
};
//求连续子数组的最大和,累积到当前位置的和sum,如果sum<0,当前片段产生了只会使后面的和更小,停止累积,清零。sum>0;继续累积。
class Solution {
public:
vector getMaxMatrix(vector>& matrix) {
int n = matrix.size(), m = matrix[0].size();
vectorcolSum(m), ans(4);
int maxSum = INT_MIN;
for (int i = 0; i < n; ++i) {
colSum.assign(m, 0); //重新统计每列的和
for (int j = i; j < n; ++j) {
int lineSum = 0, id = 0; //colSum累积的最大和
for (int k = 0; k < m; ++k) {
colSum[k] += matrix[j][k];
lineSum += colSum[k];
if (lineSum > maxSum) {//出现更大的累积和
maxSum = lineSum;
ans = { i,id,j,k };
}
if (lineSum < 0) { //累积的和小于0,不再继续累积
lineSum = 0;
id = k + 1;
}
}
}
}
return ans;
}
};