- 破解保险箱
有一个需要密码才能打开的保险箱。密码是 n 位数, 密码的每一位是 k 位序列 0, 1, …, k-1 中的一个 。你可以随意输入密码,保险箱会自动记住最后 n 位输入,如果匹配,则能够打开保险箱。举个例子,假设密码是 “345”,你可以输入 “012345” 来打开它,只是你输入了 6 个字符.请返回一个能打开保险箱的最短字符串。
欧拉回路+hierholzer算法+贪心解决
class Solution {
public:
string crackSafe(int n, int k) {
int kn = pow(k, n), kn_1 = pow(k, n-1), num[kn_1];
fill(num, num + kn_1, k-1);
string s(kn + (n-1), '0');
for(int i = n-1, node = 0; i < s.size(); ++i) {
s[i] = num[node]-- + '0';
node = node*k - (s[i-(n-1)]-'0')*kn_1 + num[node] + 1;
}
return s;
}
};
- 到达终点数字
在一根无限长的数轴上,你站在0的位置。终点在target的位置。
每次你可以选择向左或向右移动。第 n 次移动(从 1 开始),可以走 n 步。
返回到达终点需要的最小移动次数。
假设右移为正,左移为负,右移和为P,左移为N,则有
p > 0, n < 0
p + n = target
p - n = sum
所以得到2p = target + sum
又因为p<=sum
所以target<= sum
即,找到满足条件的sum大于target并二者和为偶数即可
class Solution{
public:
int reachNumber(int target){
if(target<0){
target=-target;
}
int res=1;
while(true){
int val=(res+1)*res/2;
if(val-target>=0&&(val-target)%2==0){
return res;
}
res++;
}
return 0;
}
};
- 金字塔转换矩阵
现在,我们用一些方块来堆砌一个金字塔。 每个方块用仅包含一个字母的字符串表示。
使用三元组表示金字塔的堆砌规则如下:
对于三元组 ABC ,C 为顶层方块,方块 A 、B 分别作为方块 C 下一层的的左、右子块。当且仅当 ABC 是被允许的三元组,我们才可以将其堆砌上。
初始时,给定金字塔的基层 bottom,用一个字符串表示。一个允许的三元组列表 allowed,每个三元组用一个长度为 3 的字符串表示。
如果可以由基层一直堆到塔尖就返回 true ,否则返回 false 。
哈希存储+DFS回溯
class Solution {
private:
unordered_map<string, vector<char>> dict;
public:
void init(string bottom, vector<string>& allowed) {
for(string& rule: allowed) {
dict[rule.substr(0, 2)].push_back(rule[2]);
}
}
bool pyramidTransition(string bottom, vector<string>& allowed) {
init(bottom, allowed);
return dfs(bottom, "", 0);
}
bool dfs(string bottom, string cur, int i) {
int n = bottom.size();
if(bottom.size() == 1) // 到达顶层
return true;
if(cur.size() == n - 1) // 本层构建完毕,递归构建上一层
return dfs(cur, "", 0);
if(i == n - 1) // 构建失败
return false;
string key = {
bottom[i], bottom[i+1] };
if(dict.count(key) == 0) // 无法根据任何一条规则构建
return false;
else {
for(char x: dict[key]) {
if(dfs(bottom, cur + x, i + 1))
return true;
}
}
return false;
}
};
- 设置交集大小至少为2
一个整数区间 [a, b] ( a < b ) 代表着从 a 到 b 的所有连续整数,包括 a 和 b。
给你一组整数区间intervals,请找到一个最小的集合 S,使得 S 里的元素与区间intervals中的每一个整数区间都至少有2个元素相交。输出这个最小集合S的大小。
1,第一步,排序(此类区间题基本都要排序)
2,去重,将区间完全重合的区间去掉(小区间含有的元素一定包含在大区间中的元素,贪心原理)
3,区间处理:
1)放入第一个区间,一定为区间后两位数.
2)遍历后续区间与res(结果栈)比较:
相交: 相交一个元素:加入此区间后一个元素(后元素可以用到,也是贪心体现)
相交两个元素:不要动,需要查看最后元素的前一个判断,是否和前一个元素相交
不相交: 添加后两个元素。
//第一位相同的情况下保证了小区间在前
bool cmp(vector<int> a,vector<int> b){
if(a[0]!=b[0])
return a[0]<b[0];
return a[1]<b[1];
}
class Solution {
public:
int intersectionSizeTwo(vector<vector<int>>& intervals) {
sort(intervals.begin(),intervals.end(),cmp);
vector<int> res;
vector<vector<int>> mid;
mid.push_back(intervals[0]);
//去重,保留小区间
/**
* 1 3 ,1 4 , 2 3
* <=前出,后进
* >看前元素(先比较元素,后比较后元素这种方法不可取,麻烦)
*/
for(int i=1;i<intervals.size();i++){
//cout<<intervals[i][0]<<":::"<<intervals[i][1]<<endl;
int flag=0;
while(mid.size()>0&&intervals[i][1]<=mid.back().back()){
mid.pop_back();
flag=1;
}
//flag==1说明可以进栈,==0还需要判断
if(flag==1)
mid.push_back(intervals[i]);
else{
//比较前边元素 1 3, 1 4
if(mid.back().front()<intervals[i][0])
mid.push_back(intervals[i]);
//==直接舍弃不添加进去::1 4
else{
continue;
}
}
}
/* for(int i=0;i<mid.size();i++){
cout<<mid[i][0]<<":::"<<mid[i][1]<<endl;
}*/
//获取区间值
/**
*1,放入第一个区间,一定为区间后两位数
*2,遍历后续区间与res比较:相交:相交一个元素:加入此区间后一个元素(后元素可以用到)
相交两个元素:不要动,需要查看res.back()前一个判断
不相交:添加后两个元素。
*/
res.push_back(mid[0][1]-1);
res.push_back(mid[0][1]);
//cout<<res.size();
for(int i=1;i<mid.size();i++){
if(mid[i][0]>res.back())
{
res.push_back(mid[i][1]-1);
res.push_back(mid[i][1]);
}
else if(mid[i][0]==res.back()){
res.push_back(mid[i][1]);
}
//1 3 , 2 4
else{
int tem=res.back();
res.pop_back();
//大于res中倒数第二个
if(mid[i][0]>res.back())
{
res.push_back(tem);
res.push_back(mid[i][1]);
}
//小于不处理,不需要添加,相交了两个元素
else{
res.push_back(tem);
}
}
}
return res.size();
}
};
- 特殊的二进制序列
特殊的二进制序列是具有以下两个性质的二进制序列:
0 的数量与 1 的数量相等。
二进制序列的每一个前缀码中 1 的数量要大于等于 0 的数量。
给定一个特殊的二进制序列 S,以字符串形式表示。定义一个操作 为首先选择 S 的两个连续且非空的特殊的子串,然后将它们交换。(两个子串为连续的当且仅当第一个子串的最后一个字符恰好为第二个子串的第一个字符的前一个字符。)在任意次数的操作之后,交换后的字符串按照字典序排列的最大的结果是什么?
题目听起来很复杂,其实把1看成(,0看成),那就是个括号匹配而已
class Solution {
public:
int *ne;
void arrange(string& s, int l, int r) {
if(l <= r) {
multiset<string> strs; // 注意,必须是 "multiset",以便保持重复的字符串
for(int i = l; i <= r;) {
arrange(s, i+1, ne[i]-1);
strs.insert(s.substr(i, ne[i] - i + 1));
i = ne[i] + 1;
}
int p = l;
for(auto it = strs.rbegin(); it != strs.rend(); ++it)
for(char c : *it)
s[p++] = c;
}
}
string makeLargestSpecial(string s) {
ne = new int[s.size()];
stack<int> st;
for(int i = 0; i < s.size(); ++i) {
if(s[i] == '1') {
st.push(i);
} else {
ne[st.top()] = i;
st.pop();
}
}
arrange(s, 0, (int)s.size() - 1);
return s;
}
};
- 二进制表示中质数个数的计算
给定两个整数 L 和 R ,找到闭区间 [L, R] 范围内,计算置位位数为质数的整数个数。
考虑取值范围是 1~10^6 那么 10^6 对应2的位数为 log2 (10^6)+1=20 那么这个范围内的质数只有 2,3,5,7,11,13,17,19
class Solution {
public:
int countPrimeSetBits(int L, int R) {
int a[20] = {
0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1};
int res = 0;
for (int i = L; i <= R; ++i)
{
res += a[__builtin_popcount(i)];
}
return res;
}
};
- 划分字母区间
字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。
贪心查找
class Solution {
public:
vector<int> partitionLabels(string s) {
int last[26];
int length = s.size();
for (int i = 0; i < length; i++) {
last[s[i] - 'a'] = i;
}
vector<int> partition;
int start = 0, end = 0;
for (int i = 0; i < length; i++) {
end = max(end, last[s[i] - 'a']);
if (i == end) {
partition.push_back(end - start + 1);
start = end + 1;
}
}
return partition;
}
};