- 宝石与石头
给定字符串J 代表石头中宝石的类型,和字符串 S代表你拥有的石头。 S 中每个字符代表了一种你拥有的石头的类型,你想知道你拥有的石头中有多少是宝石。
集合的简单使用
class Solution {
public:
int numJewelsInStones(string jewels, string stones)
{
int cnt = 0;
std::unordered_set<char> m_set;
for (auto c : jewels)
{
m_set.insert(c);
}
for (auto c : stones)
{
if (m_set.find(c) != m_set.end())
{
cnt++;
}
}
return cnt;
}
};
- 滑动谜题
在一个 2 x 3 的板上(board)有 5 块砖瓦,用数字 1~5 来表示, 以及一块空缺用 0 来表示.一次移动定义为选择 0 与一个相邻的数字(上下左右)进行交换.最终当板 board 的结果是 [[1,2,3],[4,5,0]] 谜板被解开。给出一个谜板的初始状态,返回最少可以通过多少次移动解开谜板,如果不能解开谜板,则返回 -1 。
通常此类问题可以使用「BFS」、「AStar 算法」、「康拓展开」进行求解。对于任意的 N * M 的数码问题,只要确保 M 为奇数,逆序对数量为偶数,必然有解(因为此时四联通的操作不会改变奇偶性)。
struct AStar {
// 曼哈顿距离
static constexpr array<array<int, 6>, 6> dist = {
{
{
0, 1, 2, 1, 2, 3},
{
1, 0, 1, 2, 1, 2},
{
2, 1, 0, 3, 2, 1},
{
1, 2, 3, 0, 1, 2},
{
2, 1, 2, 1, 0, 1},
{
3, 2, 1, 2, 1, 0}
}};
// 计算启发函数
static int getH(const string& status) {
int ret = 0;
for (int i = 0; i < 6; ++i) {
if (status[i] != '0') {
ret += dist[i][status[i] - '1'];
}
}
return ret;
};
AStar(const string& status, int g): status_{
status}, g_{
g}, h_{
getH(status)} {
f_ = g_ + h_;
}
bool operator< (const AStar& that) const {
return f_ > that.f_;
}
string status_;
int f_, g_, h_;
};
class Solution {
private:
vector<vector<int>> neighbors = {
{
1, 3}, {
0, 2, 4}, {
1, 5}, {
0, 4}, {
1, 3, 5}, {
2, 4}};;
public:
int slidingPuzzle(vector<vector<int>>& board) {
// 枚举 status 通过一次交换操作得到的状态
auto get = [&](string& status) -> vector<string> {
vector<string> ret;
int x = status.find('0');
for (int y: neighbors[x]) {
swap(status[x], status[y]);
ret.push_back(status);
swap(status[x], status[y]);
}
return ret;
};
string initial;
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 3; ++j) {
initial += char(board[i][j] + '0');
}
}
if (initial == "123450") {
return 0;
}
priority_queue<AStar> q;
q.emplace(initial, 0);
unordered_set<string> seen = {
initial};
while (!q.empty()) {
AStar node = q.top();
q.pop();
for (auto&& next_status: get(node.status_)) {
if (!seen.count(next_status)) {
if (next_status == "123450") {
return node.g_ + 1;
}
q.emplace(next_status, node.g_ + 1);
seen.insert(move(next_status));
}
}
}
return -1;
}
};
- 全局倒置与局部倒置
给你一个长度为 n 的整数数组 nums ,表示由范围 [0, n - 1] 内所有整数组成的一个排列。
全局倒置 的数目等于满足下述条件不同下标对 (i, j) 的数目:
0 <= i < j < n
nums[i] > nums[j]
局部倒置 的数目等于满足下述条件的下标 i 的数目:
0 <= i < n - 1
nums[i] > nums[i + 1]
当数组 nums 中 全局倒置 的数量等于 局部倒置 的数量时,返回 true ;否则,返回 false 。
局部倒置也是全局倒置,因此只要没有非局部倒置就是可以的
非局部倒置,就是满足 j>i+1 且 nums[i] > nums[j]
而实际就是倒序去比较,保存最小值,等价于 A[i] > min(A[i+2:*])
class Solution {
public:
bool isIdealPermutation(vector<int>& nums) {
int n = nums.size();
int low = n;
for (int i = n - 1; i >= 2; --i)
{
low = min(low, nums[i]);
if (nums[i-2] > low)
{
return false;
}
}
return true;
}
};
- 在LR字符串中交换相邻字符
在一个由 ‘L’ , ‘R’ 和 ‘X’ 三个字符组成的字符串(例如"RXXLRXRXL")中进行移动操作。一次移动操作指用一个"LX"替换一个"XL",或者用一个"XR"替换一个"RX"。现给定起始字符串start和结束字符串end,请编写代码,当且仅当存在一系列移动操作使得start可以转换成end时, 返回True。
按照L和R不会穿透过去,即L和R的相对位置不会发生改变
那么就是如果消除字符串里的X,那么字符串要相等
对于每个L和R都有一个start和end的对应,存在两个约束
L不会移动到更小的序号
R不会移动到更大的序号
string stringReplace (const string& source,
const string& toReplace,
const string& replaceWith)
{
size_t pos = 0;
size_t cursor = 0;
int repLen = toReplace.length();
stringstream builder;
do
{
pos = source.find(toReplace, cursor);
if (string::npos != pos)
{
//copy up to the match, then append the replacement
builder << source.substr(cursor, pos - cursor);
builder << replaceWith;
// skip past the match
cursor = pos + repLen;
}
}
while (string::npos != pos);
//copy the remainder
builder << source.substr(cursor);
return (builder.str());
}
class Solution {
public:
bool canTransform(string start, string end) {
if (stringReplace(start, "X", "") != stringReplace(end, "X", ""))
{
return false;
}
// 依次遍历去判断L和R是否满足相对位置需要
// end里对应的序号
int d = 0;
for (int i = 0; i < start.size(); ++i)
{
if (start[i] == 'L')
{
while (end[d] != 'L')
{
++d;
}
if (i < d)
{
// cout << "L " << i << " " << d << endl;
return false;
}
++d;
}
}
d = 0;
for (int i = 0; i < start.size(); ++i)
{
if (start[i] == 'R')
{
while (end[d] != 'R')
{
++d;
}
if (i > d)
{
// cout << "R " << i << " " << d << endl;
return false;
}
++d;
}
}
return true;
}
};
- 水位上升的泳池中游泳
在一个 N x N 的坐标方格 grid 中,每一个方格的值 grid[i][j] 表示在位置 (i,j) 的平台高度。
现在开始下雨了。当时间为 t 时,此时雨水导致水池中任意位置的水位为 t 。你可以从一个平台游向四周相邻的任意一个平台,但是前提是此时水位必须同时淹没这两个平台。假定你可以瞬间移动无限距离,也就是默认在方格内部游动是不耗时的。当然,在你游泳的时候你必须待在坐标方格里面。
你从坐标方格的左上平台 (0,0) 出发。最少耗时多久你才能到达坐标方格的右下平台 (N-1, N-1)?
二分+剪枝
class Solution {
public:
int n;
int dx[4]={
0,0,-1,1};
int dy[4]={
1,-1,0,0};
int visited[53][53];
int flag=0;
void dfs(int x,int y,vector<vector<int>>& grid,int m){
if(flag==1) return;
visited[x][y]=1;
if(x==n-1 && y==n-1){
flag=1;
return ;
}
for(int i=0;i<4;i++){
int now_x=x+dx[i];
int now_y=y+dy[i];
if(now_x>=0&&now_x<n&&now_y>=0&&now_y<n&&grid[now_x][now_y]<=m&&!visited[now_x][now_y]){
dfs(now_x,now_y,grid,m);
}
}
}
bool okk(int m,vector<vector<int>>& grid){
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
visited[i][j]=0;
}
}
flag=0;
if(grid[0][0]>m) return false;
dfs(0,0,grid,m);
return flag;
}
int swimInWater(vector<vector<int>>& grid) {
if(grid.empty()) return 0;
else{
n=grid.size();
int right=n*n-1;
int left=0;
while(left<right){
int mid=(left+right)/2;
if(okk(mid,grid)){
right=mid;
}
else{
left=mid+1;
}
}
return left;
}
}
};
- 第K个语法符号
在第一行我们写上一个 0。接下来的每一行,将前一行中的0替换为01,1替换为10。给定行数 N 和序数 K,返回第 N 行中第 K个字符。(K从1开始)
每一行的前半段就是上一行,后半段就是上一行的每个值反过来(0变1,1变0)
可以分为两种情况
如果K在前半段,所对应的值就是上一行的第K个值
如果K在后半段,可以先算出K相对于后半段的位置,然后找出上一行这个位置的值,把值反过来
class Solution {
public:
int kthGrammar(int N, int K) {
if(N == 1)return 0;
// 计算当前行的长度:2的N-1次方
int length = pow(2 ,(N - 1));
// 如果K大于长度的一半,就是K所在位置是后半段
if(K > length / 2){
// 先得到上一行的值,位置是K相对于后半段的位置
int val = kthGrammar(N - 1, K - length / 2);
// 然后把值反过来
return val == 0 ? 1 : 0;
}
// 否则前半部分
else{
// 值就是上一行K位置的值
return kthGrammar(N - 1, K);
}
}
};
- 到达终点
从点 (x, y) 可以转换到 (x, x+y) 或者 (x+y, y)。给定一个起点 (sx, sy) 和一个终点 (tx, ty),如果通过一系列的转换可以从起点到达终点,则返回 True ,否则返回 False。
class Solution {
public:
bool reachingPoints(int sx, int sy, int tx, int ty) {
// 排除边缘情况,必然不满足的
if (sx > tx || sy > ty)
{
return false;
}
if (tx == ty)
{
// 相等的时候必须都相等才可以
return sx == tx && sy == tx;
}
// 进行比较时候,总是考虑较大tx情况去处理,所以进行一次swap操作
if (tx < ty)
{
swap(tx, ty);
swap(sx, sy);
}
if (ty == sy)
{
// 不满足的情况下,必须正好能被整除才可以
return (sx >=(tx%ty) && ((tx-sx) % sy == 0));
}
return reachingPoints(sx, sy, tx % ty, ty);
}
};