557. 反转字符串中的单词 III
给定一个字符串 s ,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。
示例 1:
输入:s = “Let’s take LeetCode contest”
输出:“s’teL ekat edoCteeL tsetnoc”
示例 2:
输入: s = “Mr Ding”
输出:“rM gniD”
提示:
- 1 <= s.length <= 5 ∗ 1 0 4 5 * 10^4 5∗104
- s 包含可打印的 ASCII 字符。
- s 不包含任何开头或结尾空格。
- s 里 至少 有一个词。
- s 中的所有单词都用一个空格隔开。
根据空格找到开始和结束的位置,然后原地交换。
class Solution
{
public:
string reverseWords(string s)
{
int i = 0;
while (i < s.size())
{
int start = i;
while (i < s.size() && s[i] != ' ')
i++;
int end = i - 1;
while (start < end)
{
swap(s[start++], s[end--]);
}
while (i < s.size() && s[i] == ' ')
i++;
}
return s;
}
};
1061. 按字典序排列最小的等效字符串
给出长度相同的两个字符串s1 和 s2 ,还有一个字符串 baseStr 。
其中 s1[i] 和 s2[i] 是一组等价字符。
举个例子,如果 s1 = “abc” 且 s2 = “cde”,那么就有 ‘a’ == ‘c’, ‘b’ == ‘d’, ‘c’ == ‘e’。
等价字符遵循任何等价关系的一般规则:
自反性 :‘a’ == ‘a’
对称性 :‘a’ == ‘b’ 则必定有 ‘b’ == ‘a’
传递性 :‘a’ == ‘b’ 且 ‘b’ == ‘c’ 就表明 ‘a’ == ‘c’
例如, s1 = “abc” 和 s2 = “cde” 的等价信息和之前的例子一样,那么 baseStr = “eed” , “acd” 或 “aab”,这三个字符串都是等价的,而 “aab” 是 baseStr 的按字典序最小的等价字符串
利用 s1 和 s2 的等价信息,找出并返回 baseStr 的按字典序排列最小的等价字符串。
示例 1:
输入:s1 = “parker”, s2 = “morris”, baseStr = “parser”
输出:“makkek”
解释:根据 A 和 B 中的等价信息,我们可以将这些字符分为 [m,p], [a,o], [k,r,s], [e,i] 共 4 组。每组中的字符都是等价的,并按字典序排列。所以答案是 “makkek”。
示例 2:
输入:s1 = “hello”, s2 = “world”, baseStr = “hold”
输出:“hdld”
解释:根据 A 和 B 中的等价信息,我们可以将这些字符分为 [h,w], [d,e,o], [l,r] 共 3 组。所以只有 S 中的第二个字符 ‘o’ 变成 ‘d’,最后答案为 “hdld”。
示例 3:
输入:s1 = “leetcode”, s2 = “programs”, baseStr = “sourcecode”
输出:“aauaaaaada”
解释:我们可以把 A 和 B 中的等价字符分为 [a,o,e,r,s,c], [l,p], [g,t] 和 [d,m] 共 4 组,因此 S 中除了 ‘u’ 和 ‘d’ 之外的所有字母都转化成了 ‘a’,最后答案为 “aauaaaaada”。
提示:
1 <= s1.length, s2.length, baseStr <= 1000
s1.length == s2.length
字符串s1, s2, and baseStr 仅由从 ‘a’ 到 ‘z’ 的小写英文字母组成。
把具有等价关系的字母,放到一个集合中去,然后扫描baseStr,将其中的字符串替换成集合中的第一个字母。
#include <string>
#include <vector>
#include <iostream>
using namespace std;
// 加权并查集
class UFSets
{
public:
vector<int> parent;
UFSets(int n)
{
parent = vector<int>(n);
for (int i = 0; i < n; i++)
{
parent[i] = i;
}
}
int Find(int x)
{
if (x != parent[x])
{
// 递归进行路径压缩
parent[x] = Find(parent[x]);
}
return parent[x];
}
void Union(int x, int y)
{
int rootX = Find(x);
int rootY = Find(y);
if (rootX == rootY)
{
return;
}
else if (rootX < rootY)
{
parent[rootY] = rootX;
}
else
{
parent[rootX] = rootY;
}
}
};
class Solution
{
public:
string smallestEquivalentString(string s1, string s2, string baseStr)
{
UFSets st(26);
for (int i = 0; i < s1.size(); i++)
{
st.Union(s1[i] - 'a', s2[i] - 'a');
}
string res(baseStr.size(), ' ');
for (int i = 0; i < baseStr.size(); i++)
{
res[i] = 'a' + st.Find(baseStr[i] - 'a');
}
return res;
}
};
int main()
{
string s1 = "abc";
string s2 = "cde";
string baseStr = "eed";
Solution sol;
string res = sol.smallestEquivalentString(s1, s2, baseStr);
cout << res;
}
LCP 02. 分式化简
有一个同学在学习分式。他需要将一个连分数化成最简分数,你能帮助他吗?
连分数是形如上图的分式。在本题中,所有系数都是大于等于0的整数。
输入的cont代表连分数的系数(cont[0]代表上图的 a 0 a_0 a0,以此类推)。返回一个长度为2的数组[n, m],使得连分数的值等于n / m,且n, m最大公约数为1。
示例 1:
输入:cont = [3, 2, 0, 2]
输出:[13, 4]
解释:原连分数等价于3 + (1 / (2 + (1 / (0 + 1 / 2))))。注意[26, 8], [-13, -4]都不是正确答案。
示例 2:
输入:cont = [0, 0, 3]
输出:[3, 1]
解释:如果答案是整数,令分母为1即可。
限制:
cont[i] >= 0
1 <= cont的长度 <= 10
cont最后一个元素不等于0
答案的n, m的取值都能被32位int整型存下(即不超过
2
31
−
1
2 ^ {31} - 1
231−1)。
每次计算一个分数线,这包含两个部分,1. 分数相加,2. 颠倒分子分母,然后写一个循环,并处理好边界条件就可以了。值得注意的是,虽然题目中要求将最后的结果化简成最简分数,但是最后一步的运算为一个整数+一个整数分之一,理论上就是最简的,所以就不需要再进行辗转相除来进行化简了,因此下面代码将化简的部分进行了注释。
#include <vector>
using namespace std;
class Solution
{
// 求解最大公约数
int divide(int m, int n)
{
if (m > n)
swap(m, n);
if (n % m == 0)
return m;
return divide(m, n - m);
}
public:
vector<int> fraction(vector<int> &cont)
{
// 特殊情况,没有分数线
if (cont.size() == 1)
{
return {cont[0], 1};
}
// 一般情况
int n = 1, m = cont[cont.size() - 1];
for (int i = cont.size() - 2; i > 0; i--)
{
// 整数加分数(计算加号)
n += cont[i] * m;
// 颠倒分子分母(计算分数线)
swap(n, m);
}
n += cont[0] * m;
// 化简
// int num = divide(m, n);
// n = n / num;
// m = m / num;
// 返回结果
return {n, m};
}
};
LCP 03. 机器人大冒险
力扣团队买了一个可编程机器人,机器人初始位置在原点(0, 0)。小伙伴事先给机器人输入一串指令command,机器人就会无限循环这条指令的步骤进行移动。指令有两种:
U: 向y轴正方向移动一格
R: 向x轴正方向移动一格。
不幸的是,在 xy 平面上还有一些障碍物,他们的坐标用obstacles表示。机器人一旦碰到障碍物就会被损毁。
给定终点坐标(x, y),返回机器人能否完好地到达终点。如果能,返回true;否则返回false。
示例 1:
输入:command = “URR”, obstacles = [], x = 3, y = 2
输出:true
解释:U(0, 1) -> R(1, 1) -> R(2, 1) -> U(2, 2) -> R(3, 2)。
示例 2:
输入:command = “URR”, obstacles = [[2, 2]], x = 3, y = 2
输出:false
解释:机器人在到达终点前会碰到(2, 2)的障碍物。
示例 3:
输入:command = “URR”, obstacles = [[4, 2]], x = 3, y = 2
输出:true
解释:到达终点后,再碰到障碍物也不影响返回结果。
限制:
- 2 <= command的长度 <= 1000
- command由U,R构成,且至少有一个U,至少有一个R
- 0 < = x < = 1 e 9 , 0 < = y < = 1 e 9 0 <= x <= 1e9, 0 <= y <= 1e9 0<=x<=1e9,0<=y<=1e9
- 0 <= obstacles的长度 <= 1000
- obstacles[i]不为原点或者终点
对该过程进行模拟,依次测试false的情况,如果都不成立,最终返回true,false分为以下情况:
- 无法走到(x,y);
- 可以走到(x,y),但中间会遇到障碍;
同时,在进行求解的过程中,需要进行一些优化,比如通过除法降低模拟的轮数;在计算障碍物的时候,如果已经超过了x或y的范围,就说明不会踩到这个障碍物,continue下一个障碍。
#include <string>
#include <vector>
using namespace std;
class Solution
{
int num_U = 0, num_R = 0;
string m_command;
// 判断是否可以走到(x, y)
bool isArrive(int x, int y)
{
if (abs(x / num_R - y / num_U) <= 1)
{
int minTime = min(x / num_R, y / num_U);
int dx = x - minTime * num_R;
int dy = y - minTime * num_U;
if (dx == 0 && dy == 0)
return true;
for (int i = 0; i < m_command.size(); i++)
{
if (m_command[i] == 'U')
{
dy--;
}
else
{
dx--;
}
if (dx == 0 && dy == 0)
{
return true;
}
else if (dx < 0 || dy < 0)
{
return false;
}
}
}
return false;
}
public:
bool robot(string command, vector<vector<int>> &obstacles, int x, int y)
{
m_command = command;
// 统计command中u和r的数量
for (int i = 0; i < command.size(); i++)
{
if (command[i] == 'U')
{
num_U++;
}
else
{
num_R++;
}
}
// 判断是否可以走到目标
bool res = isArrive(x, y);
if (!res)
{
return false;
}
// 判断中间是否会遇到障碍
for (int i = 0; i < obstacles.size(); i++)
{
if (obstacles[i][0] > x || obstacles[i][1] > y)
{
continue;
}
else
{
res = isArrive(obstacles[i][0], obstacles[i][1]);
if (res)
{
return false;
}
}
}
return true;
}
};
int main()
{
string command = "URRURRR";
vector<vector<int>> obstacles = {{7, 7}, {0, 5}, {2, 7}, {8, 6}, {8, 7}, {6, 5}, {4, 4}, {0, 3}, {3, 6}};
Solution sol;
bool res = sol.robot(command, obstacles, 4915, 1966);
}