LeetCode 1460 - 1463

通过翻转子数组使两个数组相等

给出两个长度相同的数组 arr 和 target,问:每次可以在 arr 中选择的任意一段,将它们翻转,过程可以执行任意多次,最终让 arr 变得和 target 相同,能不能办到

示例1:

arr:2 4 1 3,target:1 2 3 4,可以通过翻转使得 arr 变成 1234

如果 arr 能够变成 target 的充分条件:

把 arr 看成一个集合,arr 集合中每个数的个数一定和 target 数组中每个数的个数相同,1 的个数是一样的,2 的个数也是一样的. . . 所有数的个数都是一样的

满足以上条件就一定能够通过翻转使得 arr 能够变成 target

1.类似冒泡排序,每次交换相邻两个元素,可以通过冒泡排序把任意一个序列变成有序的序列,有序的序列通过逆运算,可以把任意一个有序序列还原为任意的序列

2.从前往后看这两个序列,由于这两个数组的元素个数相同,在第二个数组中一定能够找到第一个数组中出现的元素

如何判断数组中每个数的个数一样:

1.哈希表统计每个数的个数,对比每个数的个数是否相同,时间复杂度低

2.把 target 和 arr 排序,比较两个数组的元素是否相同:vector 内部实现了相等的比较操作,如果两个数组的元素个数一样,排序后一定相等的

class Solution {
public:
    bool canBeEqual(vector<int>& target, vector<int>& arr) {
        //排序
        sort(target.begin(),target.end());
        sort(arr.begin(),arr.end());
        //判断
        return target == arr;
    }
};

检查一个字符串是否包含所有长度为 k 的二进制子串 

给出一个 01 串和一个整数 k,问所有长度是 k 的二进制串,是不是都是 s 的子串

示例1:

k = 2,所有长度是 k 的二进制串有 2^k 个,即 2^2 == 4,00、01、10、11,判断这四个是不是 s 串的子串,子串一定是连续的一段,和子序列不一样

注意可以重叠,示例1 中所有长度是 2 的字符串都在 s 中出现过,所以返回 true,如果某一个没有出现过就返回 false

s 的范围是 50w,时间复杂度要控制在 O ( n ) 以内

枚举每一个长度是 k 的二进制串,k 最大是 20,最终要枚举 2^20 次方个串,每次枚举完要在原串中搜索一遍(时间复杂度会很高,会超时),看它是否存在

1.遍历 s 当中所有长度是 k 的二进制串,把它们存储到哈希表中,查询每个子串时直接在哈希表中查询,时间复杂度为 O(1)

优化:由于把 s 当中所有长度是 k 的二进制串,全部存到了哈希表中,需要判断哈希表中是不是包含了所有长度是 k 的二进制串,没必要再去扫描一遍,判断每个串是否存在,直接判断这个集合的大小是不是 2^k 即可

2.最后判断哈希表中的元素个数是不是 2^k 次方

在存储到哈希表中时,不要直接存 01 串,可以把这个 01 串,转换成一个十进制整数,把这个整数存储到哈希表中

开一个 int 类型的哈希表,每次枚举一个长度是 k 的二进制串时,把它转换成一个整数,把这个整数存储到哈希表中

c++ unordered_set的用法_kzan的博客-CSDN博客_unordered_set用法

位运算符之---左移右移运算符(简单易懂)_ShenMingYi_的博客-CSDN博客_位运算符左移右移怎么运算

+、-、*、/ 的优先级高于 <<,== 的优先级比 << 低

class Solution {
public:
    bool hasAllCodes(string s, int k) {
        //开一个 int 类型哈希表
        unordered_set<int> S;
        //枚举所有长度是 k 的串,把串存到 w 中
        for(int i = 0,w = 0;i < s.size();i++ ) {
            //每次把二进制的最后一个位加过来
            //w 用于存储字符串转换为十进制之后的数
            w = w * 2 + s[i] - '0';
            //每次把下一个数加过来,再把上一个数删掉
            if(i >= k) w -=( s[i - k] - '0' )<< k;
            //已经有 k 位
            if(i >= k - 1) S.insert(w);
        }
        return S.size() == (1 << k);
    }
};
int main()
{
    string s = "00110110";
    int k = 2;
    unordered_set<int> S;
    for (int i = 0, w = 0; i < s.size(); i++) {
        w = w * 2 + s[i] - '0';
        if (i >= k) w -= s[i - k] - '0' << k;
        if (i >= k - 1) { cout << w << "\t";  S.insert(w); }
    }
    cout << endl;
    cout<<S.size();
}
/*输出*/

0       1       3       2       1       3       2
4

课程表 IV

 

告诉我们两门课,问第一门课是不是第二门课的先修课

有 n 门课,课程编号 0 ~ n -1,告诉了:1.先修课的总数 n        2.先修课的依赖关系

0 的先修课是 1,1 的先修课是 2,2 也算是 0 的先修课,不仅要找 1,还要找 1 前面的先修课,先修课的先修课也是你的先修课. . .

示例1:

问:2 是不是 0 的先修课,即从 2 出发能不能到 0 的位置,相当于给了一个有向图,给了很多询问,每个询问是一个 a,一个 b,问:从 a 出发,能不能通过某些路径走到 b?

最多只有 100 个点,边数最多是 n^2,一定没有环,没有重边,最多有 10000 个询问,时间复杂度为 O( n^3 )

如果形成环,环中的任何一门课都不能修

传递闭包问题,Floyd 算法的应用

Floyd 算法:说到 Floyd,想必大家首先想到的是用于求解两点间的最短距离。但基于 Floyd 算法的思想,它求解两点间最短路径时实际上是运用 dp 思想,通过一个中间节点的引入,如点 A->C,我们可以引入一个中间节点 B,求出 A->B->C 的距离,将其与 A->C 的距离相比更新最短路径。本题之所以可以运用Floyd算法解答,是因为本题只需要判断两点的连通性,例如:A->C 为直接连接,A->B->C 中 A->C 即为间接连通。

三重循环,第一重循环枚举中介点 k,第二重循环枚举每条边的起点,第三重循环枚举每条边的终点,每次用两条边更新第三条边即可

bfs:先枚举所有起点,对于每个起点,从这个起点去宽搜或者深搜一遍,搜一下这个起点能走到哪些点,时间复杂度 O(点数n + 边数m),把它存储下来→ 先预处理再询问

弗洛伊德(Floyd)算法求图的最短路径_JeffCoding的博客-CSDN博客_弗洛伊德算法求最短路径

class Solution {
public:
    vector<bool> checkIfPrerequisite(int numCourses, vector<vector<int>>& 
    prerequisites, vector<vector<int>>& queries) {
        //bool 二维数组表示 a 能不能到 b
        vector<vector<bool>> d(numCourses,vector<bool>(numCourses));
        //存储所有的边
        for(auto e : prerequisites) d[e[0]][e[1]] = true;
        //Floyd算法
        for(int k = 0;k < numCourses;k++ )
            for(int i = 0;i < numCourses;i++ )
                for(int j = 0;j < numCourses;j++ )
                    //如果从 i 到 k 有边并且从 k 到 j 有边
                    if(d[i][k] && d[k][j])
                        //从 i 到 j 就有边
                        d[i][j] = true;
        //枚举所有的询问
        vector<bool> res;
        for(auto q : queries) res.push_back(d[q[0]][q[1]]);
        return res;
    }
};

摘樱桃 II  

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qiuqiuyaq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值