力扣链接
给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [−2^31, 2^31 − 1] ,就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)。
示例 1:
输入:x = 123
输出:321
示例 2:
输入:x = -123
输出:-321
示例 3:
输入:x = 120
输出:21
示例 4:
输入:x = 0
输出:0
提示:
-231 <= x <= 231 - 1
思路:
代码:
class Solution {
public:
int reverse(int x) {
long n = 0;
while(x != 0){
n = x%10 + n*10;
x = x/10;
}
return n > INT_MAX || n < INT_MIN ? 0 : n;
// return (int)n == n ? n:0;
}
};
复杂度分析:
时间复杂度:O(log∣x∣)。翻转的次数即 x 十进制的位数。
空间复杂度:O(1)。
力扣链接
给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。
回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。例如,121 是回文,而 123 不是。
示例 1:
输入:x = 121
输出:true
示例 2:
输入:x = -121
输出:false
解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
示例 3:
输入:x = 10
输出:false
解释:从右向左读, 为 01 。因此它不是一个回文数。
示例 4:
输入:x = -101
输出:false
提示:
-231 <= x <= 231 - 1
进阶:你能不将整数转为字符串来解决这个问题吗?
class Solution {
public:
bool isPalindrome(int x) {
string xs = to_string(x);
int n = xs.size();
for(int i = 0,j=n-1;i<j;i++,j--){
if(xs[i] != xs[j]) return false;
}
return true;
}
};
代码:
class Solution {
public:
bool isPalindrome(int x) {
// 特殊情况:
// 如上所述,当 x < 0 时,x 不是回文数。
// 同样地,如果数字的最后一位是 0,为了使该数字为回文,
// 则其第一位数字也应该是 0
// 只有 0 满足这一属性
if (x < 0 || (x % 10 == 0 && x != 0)) {
return false;
}
int revertedNumber = 0;
while (x > revertedNumber) {
revertedNumber = revertedNumber * 10 + x % 10;
x /= 10;
}
// 当数字长度为奇数时,我们可以通过 revertedNumber/10 去除处于中位的数字。
// 例如,当输入为 12321 时,在 while 循环的末尾我们可以得到 x = 12,revertedNumber = 123,
// 由于处于中位的数字不影响回文(它总是与自己相等),所以我们可以简单地将其去除。
return x == revertedNumber || x == revertedNumber / 10;
}
};
复杂度分析:
时间复杂度:O(logn),对于每次迭代,我们会将输入除以 1010,因此时间复杂度为 O(logn)。
空间复杂度:O(1)。我们只需要常数空间存放若干变量。
力扣链接
请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。
函数 myAtoi(string s) 的算法如下:
读入字符串并丢弃无用的前导空格
检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
将前面步骤读入的这些数字转换为整数(即,“123” -> 123, “0032” -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
如果整数数超过 32 位有符号整数范围 [−231, 231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被固定为 −231 ,大于 231 − 1 的整数应该被固定为 231 − 1 。
返回整数作为最终结果。
注意:
本题中的空白字符只包括空格字符 ’ ’ 。
除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。
代码:
class Solution {
public:
int myAtoi(string s) {
//有限状态机
long result = 0;
int state = 0;//0:初始状态 1:正整数状态 2:负数状态
for(auto& c:s){
if(state == 0 && c == ' ') continue;
else if(state == 0 && c == '+') state = 1;
else if(state == 0 && c == '-') state = 2;
else if(c >= '0' && c <= '9'){
if(state == 0) state = 1;
int temp = c - '0';
result = result*10 + temp;
if(result > INT_MAX) break;
}
else break;
}
if(state == 1 && result > INT_MAX) result = INT_MAX;
else if(state == 2){
result = -result;
if(result < INT_MIN) result = INT_MIN;
}
return result;
}
};
复杂度分析:
时间复杂度:O(n),其中 n为字符串的长度。我们只需要依次处理所有的字符,处理每个字符需要的时间为O(1)。
空间复杂度:O(1)。自动机的状态只需要常数空间存储。
力扣链接
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给你一个整数,将其转为罗马数字。
示例 1:
输入: num = 3
输出: “III”
示例 2:
输入: num = 4
输出: “IV”
示例 3:
输入: num = 9
输出: “IX”
示例 4:
输入: num = 58
输出: “LVIII”
解释: L = 50, V = 5, III = 3.
示例 5:
输入: num = 1994
输出: “MCMXCIV”
解释: M = 1000, CM = 900, XC = 90, IV = 4.
提示:
1 <= num <= 3999
思路:
罗马数字由 7 个不同的单字母符号组成,每个符号对应一个具体的数值。此外,减法规则(如问题描述中所述)给出了额外的 6 个复合符号。这给了我们总共 13 个独特的符号(每个符号由 1 个或 2 个字母组成),如下图所示。
代码:
class Solution {
public:
string intToRoman(int num) {
int intValue[] = {1000,900,500,400,100,90,50,40,10,9,5,4,1};
string romanValue[] = {"M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"};
string result;
for(int i = 0;i<13;i++){
while(num>=intValue[i]){
result += romanValue[i];
num -= intValue[i];
}
}
return result;
}
};
复杂度分析:
时间复杂度:O(1)。计算量与输入数字的大小无关。
空间复杂度:O(1)。
力扣链接
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 2 写做 II ,即为两个并列的 1 。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给定一个罗马数字,将其转换成整数。
示例 1:
输入: s = “III”
输出: 3
示例 2:
输入: s = “IV”
输出: 4
示例 3:
输入: s = “IX”
输出: 9
示例 4:
输入: s = “LVIII”
输出: 58
解释: L = 50, V= 5, III = 3.
示例 5:
输入: s = “MCMXCIV”
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.
提示:
1 <= s.length <= 15
s 仅含字符 (‘I’, ‘V’, ‘X’, ‘L’, ‘C’, ‘D’, ‘M’)
题目数据保证 s 是一个有效的罗马数字,且表示整数在范围 [1, 3999] 内
题目所给测试用例皆符合罗马数字书写规则,不会出现跨位等情况。
IL 和 IM 这样的例子并不符合题目要求,49 应该写作 XLIX,999 应该写作 CMXCIX 。
关于罗马数字的详尽书写规则,可以参考 罗马数字 - Mathematics 。
思路:
代码:
class Solution {
public:
int romanToInt(string s) {
unordered_map<char,int> umap ={
{'I', 1},
{'V', 5},
{'X', 10},
{'L', 50},
{'C', 100},
{'D', 500},
{'M', 1000},
};
int result = 0;
int n = s.size();
for(int i = 0;i<n;i++){
if(i<n-1 && umap[s[i]] < umap[s[i+1]]){
result -= umap[s[i]];
}else result += umap[s[i]];
}
return result;
}
};
复杂度分析:
时间复杂度:O(n) ,其中 n 是字符串 s 的长度。
空间复杂度:O(1)。
力扣链接
给定一个正整数 n ,输出外观数列的第 n 项。
「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。
你可以将其视作是由递归公式定义的数字字符串序列:
countAndSay(1) = “1”
countAndSay(n) 是对 countAndSay(n-1) 的描述,然后转换成另一个数字字符串。
前五项如下:
1
11
21
1211
111221
第一项是数字 1
描述前一项,这个数是 1 即 “ 一 个 1 ”,记作 “11”
描述前一项,这个数是 11 即 “ 二 个 1 ” ,记作 “21”
描述前一项,这个数是 21 即 “ 一 个 2 + 一 个 1 ” ,记作 “1211”
描述前一项,这个数是 1211 即 “ 一 个 1 + 一 个 2 + 二 个 1 ” ,记作 “111221”
要 描述 一个数字字符串,首先要将字符串分割为 最小 数量的组,每个组都由连续的最多 相同字符 组成。然后对于每个组,先描述字符的数量,然后描述字符,形成一个描述组。要将描述转换为数字字符串,先将每组中的字符数量用数字替换,再将所有描述组连接起来。
例如,数字字符串 “3322251” 的描述如下图:
示例 1:
输入:n = 1
输出:“1”
解释:这是一个基本样例。
示例 2:
输入:n = 4
输出:“1211”
解释:
countAndSay(1) = “1”
countAndSay(2) = 读 “1” = 一 个 1 = “11”
countAndSay(3) = 读 “11” = 二 个 1 = “21”
countAndSay(4) = 读 “21” = 一 个 2 + 一 个 1 = “12” + “11” = “1211”
提示:
1 <= n <= 30
思路:
一个朴素的想法是:根据题意进行模拟,从起始条件 k = 1 时 ans = “1” 出发,逐步递推到 k = n 的情况,对于第 k 项而言,其实就是对第 k - 1 项的「连续段」的描述,而求「连续段」长度,可以使用双指针实现。
代码:
class Solution {
public:
string countAndSay(int n) {
string pre = "1";
for(int i = 2;i<=n;i++){
string cur = "";
int m = pre.size();
for(int j = 0;j<m;){
int k = j + 1;
while(k<m && pre[j] == pre[k]){
k++;
}
cur += to_string(k-j)+pre[j];
j = k;
}
pre = cur;
}
return pre;
}
};
时间复杂度:O(n^2)
空间复杂度:O(n)
力扣链接
给你一个字符串 s ,逐个翻转字符串中的所有 单词 。
单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。
请你返回一个翻转 s 中单词顺序并用单个空格相连的字符串。
说明:
输入字符串 s 可以在前面、后面或者单词间包含多余的空格。
翻转后单词间应当仅用一个空格分隔。
翻转后的字符串中不应包含额外的空格。
示例 1:
输入:s = “the sky is blue”
输出:“blue is sky the”
示例 2:
输入:s = " hello world "
输出:“world hello”
解释:输入字符串可以在前面或者后面包含多余的空格,但是翻转后的字符不能包括。
示例 3:
输入:s = “a good example”
输出:“example good a”
解释:如果两个单词间有多余的空格,将翻转后单词间的空格减少到只含一个。
示例 4:
输入:s = " Bob Loves Alice "
输出:“Alice Loves Bob”
示例 5:
输入:s = “Alice does not even like bob”
输出:“bob like even not does Alice”
提示:
1 <= s.length <= 104
s 包含英文大小写字母、数字和空格 ’ ’
s 中 至少存在一个 单词
进阶:
请尝试使用 O(1) 额外空间复杂度的原地解法。
思路:
(1)这道题目可以说是综合考察了字符串的多种操作。
一些同学会使用split库函数,分隔单词,然后定义一个新的string字符串,最后再把单词倒序相加,那么这道题题目就是一道水题了,失去了它的意义。所以这里还是提高一下本题的难度:不要使用辅助空间,空间复杂度要求为 O ( 1 ) O(1) O(1)。
不能使用辅助空间之后,那么只能在原字符串上下功夫了。想一下,我们将整个字符串都反转过来,那么单词的顺序指定是倒序了,只不过单词本身也倒序了,那么再把单词反转一下,单词不就正过来了。
所以解题思路如下:
移除多余空格
将整个字符串反转
将每个单词反转
举个例子,源字符串为:"the sky is blue "
移除多余空格 : "the sky is blue"
字符串反转:"eulb si yks eht"
单词反转:"blue is sky the"
这样我们就完成了翻转字符串里的单词。
思路很明确了,我们说一说代码的实现细节,就拿移除多余空格来说,一些同学会上来写如下代码:
void removeExtraSpaces(string& s) {
for (int i = s.size() - 1; i > 0; i--) {
if (s[i] == s[i - 1] && s[i] == ' ') {
s.erase(s.begin() + i);
}
}
// 删除字符串最后面的空格
if (s.size() > 0 && s[s.size() - 1] == ' ') {
s.erase(s.begin() + s.size() - 1);
}
// 删除字符串最前面的空格
if (s.size() > 0 && s[0] == ' ') {
s.erase(s.begin());
}
}
逻辑很简单,从前向后遍历,遇到空格了就erase。如果不仔细琢磨一下erase的时间复杂读,还以为以上的代码是 O ( n ) O(n) O(n)的时间复杂度呢。想一下真正的时间复杂度是多少,一个erase本来就是 O ( n ) O(n) O(n)的操作,erase实现原理题目:数组:就移除个元素很难么? (opens new window),最优的算法来移除元素也要 O ( n ) O(n) O(n)。
erase操作上面还套了一个for循环,那么以上代码移除冗余空格的代码时间复杂度为 O ( n 2 ) O(n^2) O(n2)。那么使用双指针法来去移除空格,最后resize(重新设置)一下字符串的大小,就可以做到 O ( n ) O(n) O(n)的时间复杂度。
(2) 那么使用双指针来移除冗余空格代码如下: fastIndex走的快,slowIndex走的慢,最后slowIndex就标记着移除多余空格后新字符串的长度。
void removeExtraSpaces(string& s) {
int slowIndex = 0, fastIndex = 0; // 定义快指针,慢指针
// 去掉字符串前面的空格
while (s.size() > 0 && fastIndex < s.size() && s[fastIndex] == ' ') {
fastIndex++;
}
for (; fastIndex < s.size(); fastIndex++) {
// 去掉字符串中间部分的冗余空格
if (fastIndex - 1 > 0
&& s[fastIndex - 1] == s[fastIndex]
&& s[fastIndex] == ' ') {
continue;
} else {
s[slowIndex++] = s[fastIndex];
}
}
if (slowIndex - 1 > 0 && s[slowIndex - 1] == ' ') { // 去掉字符串末尾的空格
s.resize(slowIndex - 1);
} else {
s.resize(slowIndex); // 重新设置字符串大小
}
}
有的同学可能发现用erase来移除空格,在leetcode上性能也还行。主要是以下几点;:
leetcode上的测试集里,字符串的长度不够长,如果足够长,性能差距会非常明显。
leetcode的测程序耗时不是很准确的。
代码:
class Solution {
public:
void removeExtraWords(string& s){
int slowIndex = 0, fastIndex = 0;
//去掉最前面的空格
while(s.size() > 0 && fastIndex < s.size() && s[fastIndex] == ' ') fastIndex++;
//去掉之间的空格
for(;fastIndex<s.size();fastIndex++){
if(fastIndex > 0 && s[fastIndex] == s[fastIndex-1] && s[fastIndex] == ' '){
continue;
}else{
s[slowIndex++] = s[fastIndex];
}
}
//去掉最后的空格
if(slowIndex>0 && s[slowIndex-1] == ' '){
s.resize(slowIndex-1);
}else{
s.resize(slowIndex);
}
}
string reverseWords(string s) {
removeExtraWords(s);//去掉多于空格
reverse(s.begin(),s.end());//翻转整个字符串
//开始处理每个单词的翻转
int start = 0;
for(int i = 0;i<s.size();i++){
if(s[i] == ' '){
reverse(s.begin()+start, s.begin()+i);
start = i+1;
}
if(i == s.size() - 1){
reverse(s.begin()+start,s.begin()+i+1);
}
}
return s;
}
};
复杂度分析:
时间复杂度:O(n),其中 n 为输入字符串的长度。
空间复杂度:Java 和 Python 的方法需要 O(n)的空间来存储字符串,而 C++ 方法只需要 O(1) 的额外空间来存放若干变量。
力扣链接
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。
示例 1:
输入: s = “abcdefg”, k = 2
输出: “cdefgab”
示例 2:
输入: s = “lrloseumgh”, k = 6
输出: “umghlrlose”
限制:
1 <= k < s.length <= 10000
思路:
为了让本题更有意义,提升一下本题难度:**不能申请额外空间,只能在本串上操作。**不能使用额外空间的话,模拟在本串操作要实现左旋转字符串的功能还是有点困难的。
那么我们可以想一下上一题目字符串:花式反转还不够! (opens new window)中讲过,使用整体反转+局部反转就可以实现,反转单词顺序的目的。
这道题目也非常类似,依然可以通过局部反转+整体反转 达到左旋转的目的。
具体步骤为:
反转区间为前n的子串
反转区间为n到末尾的子串
反转整个字符串
最后就可以得到左旋n的目的,而不用定义新的字符串,完全在本串上操作。
例如 :示例1中 输入:字符串abcdefg,n=2
思路明确之后,那么代码实现就很简单了
代码:
class Solution {
public:
string reverseLeftWords(string s, int n) {
reverse(s.begin(),s.begin()+n);
reverse(s.begin()+n,s.end());
reverse(s.begin(),s.end());
return s;
}
};
力扣链接
给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]
示例 2:
输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]
提示:
1 <= nums.length <= 105
-231 <= nums[i] <= 231 - 1
0 <= k <= 105
进阶:
尽可能想出更多的解决方案,至少有 三种 不同的方法可以解决这个问题。
你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?
思路:
在字符串:剑指Offer58-II.左旋转字符串中,我们提到,如下步骤就可以坐旋转字符串:
反转区间为前n的子串
反转区间为n到末尾的子串
反转整个字符串
本题是右旋转,其实就是反转的顺序改动一下,优先反转整个字符串,步骤如下:
反转整个字符串
反转区间为前k的子串
反转区间为k到末尾的子串
代码:
class Solution {
public:
void rotate(vector<int>& nums, int k) {
int n = nums.size();
k = k % n;
reverse(nums.begin(),nums.end());
reverse(nums.begin(),nums.begin()+k);
reverse(nums.begin()+k,nums.end());
}
};
力扣链接
给定一组非负整数 nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数。
注意:输出结果可能非常大,所以你需要返回一个字符串而不是整数。
示例 1:
输入:nums = [10,2]
输出:“210”
示例 2:
输入:nums = [3,30,34,5,9]
输出:“9534330”
示例 3:
输入:nums = [1]
输出:“1”
示例 4:
输入:nums = [10]
输出:“10”
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 109
思路:
今天的题目,让我们将一组数字重新组合,构成一个最大的整数。由于构成的整数可能非常大,所以返回结果需要字符串格式。
输入数组的长度会达到 100,如果你想用 DFS 找出所有可以拼接的结果然后求最大,那么就走偏了,一定会超时,而且会超出空间限制。
我们分析一下规律:
当 nums = [10,2] 时,结果是 210。其实我们就是在比较 [10,2] 这两个数字能组合成的 210 和 102 哪个数字更大,显然 210 更大,所以需要把 2 放在前面。
为了避免用 int 型或者 long 型越界,所以我们需要把数字先转成字符串。 然后可以把待比较的两个数字 x,y 组合成两个新的数字 string(x) + string(y) 和 string(y) + string(x) ,比较一下哪种组合构成的数字更大。
你可能会有疑问:如果拼接得到的字符串结果更大的话,那么原本的整型的数字拼接结果也一定更大吗?比如 “210” > “102”,那么一定能得到 210 > 102 么?
答案是肯定的:首先拼接成的两个字符串一定是等长的。 等长的字符串在比较的时候,是按照字符串的各个字符从前向后逐个比较的,所以相当于先比较了百分位,然后比较十分位,最后比较个位。 所以在字符串等长的情况下,字符串大,那么对应的整型也更大。但两个不等长的字符串就没有这个结论了, 比如 “2” > “10”,但是 2 < 10。
综上,我们按照下面的步骤:
本题有个坑:如果输入的 nums 中只有 0 时,上面的结果会返回 “00” 这样的全零字符串。解决办法是可以提前判断 nums 是否全部为零,或者可以判断最终拼接完成的字符串中首位是不是 “0”,因为如果 nums 至少有一个数字不是 0, 那么该数字一定都会排在所有的 0 的前面。
代码:
class Solution {
public:
string largestNumber(vector<int>& nums) {
vector<string> strVec;
for(auto num:nums){
strVec.push_back(to_string(num));
}
sort(strVec.begin(),strVec.end(),[&](const string& a, const string& b){
return a+b > b+a;
});
string res = "";
for(auto& str:strVec){
res += str;
}
if(res[0] == '0') return "0";
return res;
}
};
力扣链接
总共有 n 个颜色片段排成一列,每个颜色片段要么是 ‘A’ 要么是 ‘B’ 。给你一个长度为 n 的字符串 colors ,其中 colors[i] 表示第 i 个颜色片段的颜色。
Alice 和 Bob 在玩一个游戏,他们 轮流 从这个字符串中删除颜色。Alice 先手 。
如果一个颜色片段为 ‘A’ 且 相邻两个颜色 都是颜色 ‘A’ ,那么 Alice 可以删除该颜色片段。Alice 不可以 删除任何颜色 ‘B’ 片段。
如果一个颜色片段为 ‘B’ 且 相邻两个颜色 都是颜色 ‘B’ ,那么 Bob 可以删除该颜色片段。Bob 不可以 删除任何颜色 ‘A’ 片段。
Alice 和 Bob 不能 从字符串两端删除颜色片段。
如果其中一人无法继续操作,则该玩家 输 掉游戏且另一玩家 获胜 。
假设 Alice 和 Bob 都采用最优策略,如果 Alice 获胜,请返回 true,否则 Bob 获胜,返回 false。
示例 1:
输入:colors = “AAABABB”
输出:true
解释:
AAABABB -> AABABB
Alice 先操作。
她删除从左数第二个 ‘A’ ,这也是唯一一个相邻颜色片段都是 ‘A’ 的 ‘A’ 。
现在轮到 Bob 操作。
Bob 无法执行任何操作,因为没有相邻位置都是 ‘B’ 的颜色片段 ‘B’ 。
因此,Alice 获胜,返回 true 。
示例 2:
输入:colors = “AA”
输出:false
解释:
Alice 先操作。
只有 2 个 ‘A’ 且它们都在字符串的两端,所以她无法执行任何操作。
因此,Bob 获胜,返回 false 。
示例 3:
输入:colors = “ABBBBBBBAAA”
输出:false
解释:
ABBBBBBBAAA -> ABBBBBBBAA
Alice 先操作。
她唯一的选择是删除从右数起第二个 ‘A’ 。
ABBBBBBBAA -> ABBBBBBAA
接下来轮到 Bob 操作。
他有许多选择,他可以选择任何一个 ‘B’ 删除。
然后轮到 Alice 操作,她无法删除任何片段。
所以 Bob 获胜,返回 false 。
提示:
1 <= colors.length <= 105
colors 只包含字母 ‘A’ 和 ‘B’
代码:
class Solution {
public:
bool winnerOfGame(string colors) {
if(colors.size() < 3) return false;
int cnt = 0;
for(int i = 0;i<colors.size()-2;i++){
if(colors[i] == 'A' && colors[i+1] == 'A' && colors[i+2] == 'A') {
cnt++;
continue;
}
if(colors[i] == 'B' && colors[i+1] == 'B' && colors[i+2] == 'B') {
cnt--;
continue;
}
}
return cnt > 0;
}
};
实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn )。
示例 1:
输入:x = 2.00000, n = 10
输出:1024.00000
示例 2:
输入:x = 2.10000, n = 3
输出:9.26100
示例 3:
输入:x = 2.00000, n = -2
输出:0.25000
解释:2-2 = 1/22 = 1/4 = 0.25
提示:
-100.0 < x < 100.0
-231 <= n <= 231-1
-104 <= xn <= 104
class Solution {
public:
//分治 递归
double Pow(double x, int n){
if(n == 0) return 1;
if(n % 2 == 1) return x*pow(x, n-1);
double half = pow(x, n/2);//分
return half*half;//运算+合并
}
double myPow(double x, int n) {
long long N = n;
return N >= 0 ? pow(x,n) : 1.0/pow(x,-N);
}
};
力扣链接
给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。
示例 1:
输入:x = 4
输出:2
示例 2:
输入:x = 8
输出:2
解释:8 的算术平方根是 2.82842…, 由于返回类型是整数,小数部分将被舍去。
提示:
0 <= x <= 231 - 1
class Solution {
public:
int mySqrt(int x) {
//二分查找
int l = 0, r = x;
int res = 0;
while(l <= r){
int mid = l + (r-l)/2;
if((long long)mid*mid <= x ){
res = mid;
l = mid + 1;
}else{
r = mid-1;
}
}
return res;
}
};
class Solution {
public:
int mySqrt(int x) {
//牛顿法
if(x == 0) return 0;
double c = x;
double x0 = c;
double xi;
while(true){
xi = 0.5*(c/x0 + x0);
if(abs(xi-x0) < 1e-7) break;
x0 = xi;
}
return int(x0);
}
};
力扣链接
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。
示例 1:
输入: num1 = “2”, num2 = “3”
输出: “6”
示例 2:
输入: num1 = “123”, num2 = “456”
输出: “56088”
提示:
1 <= num1.length, num2.length <= 200
num1 和 num2 只能由数字组成。
num1 和 num2 都不包含任何前导零,除了数字0本身。
class Solution {
public:
string multiply(string num1, string num2) {
int n1 = num1.size(), n2 = num2.size();
string res(n1+n2,'0');
for(int i = n2-1;i>=0;i--){
for(int j = n1-1;j>=0;j--){
int mul = (num1[j]-'0')*(num2[i]-'0') + res[i+j+1] - '0';
res[i+j+1] = mul%10 + '0';
res[i+j] += mul/10;
}
}
for(int i = 0;i<n1+n2;i++){
if(res[i] != '0'){
return res.substr(i);
}
}
return "0";
}
};
力扣链接
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。
你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。
示例 1:
输入:num1 = “11”, num2 = “123”
输出:“134”
示例 2:
输入:num1 = “456”, num2 = “77”
输出:“533”
示例 3:
输入:num1 = “0”, num2 = “0”
输出:“0”
提示:
1 <= num1.length, num2.length <= 104
num1 和num2 都只包含数字 0-9
num1 和num2 都不包含任何前导零
class Solution {
public:
string addStrings(string num1, string num2) {
int n1 = num1.size(), n2 = num2.size(), add = 0;
string res = "";
for(int i = n1-1,j = n2-1;i>=0 || j>=0 || add > 0;i--,j--){
int x = i >= 0 ? num1[i] - '0':0;
int y = j >= 0 ? num2[j] - '0':0;
int cur = x + y + add;
res += cur%10 + '0';
add = cur / 10;
}
reverse(res.begin(),res.end());
return res;
}
};
力扣链接
给定一个经过编码的字符串,返回它解码后的字符串。
编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。
示例 1:
输入:s = “3[a]2[bc]”
输出:“aaabcbc”
示例 2:
输入:s = “3[a2[c]]”
输出:“accaccacc”
示例 3:
输入:s = “2[abc]3[cd]ef”
输出:“abcabccdcdcdef”
示例 4:
输入:s = “abc3[cd]xyz”
输出:“abccdcdcdxyz”
提示:
1 <= s.length <= 30
s 由小写英文字母、数字和方括号 ‘[]’ 组成
s 保证是一个 有效 的输入。
s 中所有整数的取值范围为 [1, 300]
class Solution {
public:
string decodeString(string s) {
stack<pair<string,int>> st;//左括号前的字符串和数字
string res = "";
int num = 0;
for(char c:s){
if(c >= '0' && c <= '9'){
num = 10* num + c - '0';
}else if(c == '['){
st.push({res,num});
res = "";
num = 0;
}else if(c == ']'){
string tmp = res;
for(int i = 0;i<st.top().second-1;i++){
res += tmp;
}
res = st.top().first + res;
st.pop();
}else{
res += c;
}
}
return res;
}
};
力扣链接
给定方法 rand7 可生成 [1,7] 范围内的均匀随机整数,试写一个方法 rand10 生成 [1,10] 范围内的均匀随机整数。
你只能调用 rand7() 且不能调用其他方法。请不要使用系统的 Math.random() 方法。
每个测试用例将有一个内部参数 n,即你实现的函数 rand10() 在测试时将被调用的次数。请注意,这不是传递给 rand10() 的参数。
示例 1:
输入: 1
输出: [2]
示例 2:
输入: 2
输出: [2,8]
示例 3:
输入: 3
输出: [3,8,10]
提示:
1 <= n <= 105
进阶:
rand7()调用次数的 期望值 是多少 ?
你能否尽量少调用 rand7() ?
参考
rand7() 构造 rand10()
// The rand7() API is already defined for you.
// int rand7();
// @return a random integer in the range 1 to 7
class Solution {
public:
int rand10() {
int first, second;
while((first = rand7()) > 6);//拒绝7
while((second = rand7()) > 5);//拒绝6,7
return (first&1) == 1? second:5+second;
}
};
力扣链接
给你一个整数 n ,请你在无限的整数序列 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, …] 中找出并返回第 n 位上的数字。
示例 1:
输入:n = 3
输出:3
示例 2:
输入:n = 11
输出:0
解释:第 11 位数字在序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, … 里是 0 ,它是 10 的一部分。
提示:
1 <= n <= 231 - 1
class Solution {
public:
int findNthDigit(int n) {
int len = 1, weight = 1;
//遍历到n位数字所在的k位数、
while (n > (long long)9*len*weight){
n-=9*len*weight;
len++;
weight*=10;
}
//找到具体的数
int curNum = (n-1)/len + weight;
//找到具体数中的位数
int rest = (n-1)%len;
string result = to_string(curNum);
return result[rest]-'0';
}
};
力扣链接
给定一个正整数,检查它的二进制表示是否总是 0、1 交替出现:换句话说,就是二进制表示中相邻两位的数字永不相同。
示例 1:
输入:n = 5
输出:true
解释:5 的二进制表示是:101
示例 2:
输入:n = 7
输出:false
解释:7 的二进制表示是:111.
示例 3:
输入:n = 11
输出:false
解释:11 的二进制表示是:1011.
提示:
1 <= n <= 231 - 1
class Solution {
public:
bool hasAlternatingBits(int n) {
int pre = -1;
while(n){
int res = n & 1;
if(pre == -1 || res != pre) pre = res;
else return false;
n >>= 1;
}
return true;
}
};
力扣链接
编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。
提示:
请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。
示例 1:
输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 ‘1’。
示例 2:
输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 ‘1’。
示例 3:
输入:11111111111111111111111111111101
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 ‘1’。
提示:
输入必须是长度为 32 的 二进制串 。
进阶:
如果多次调用这个函数,你将如何优化你的算法?
class Solution {
public:
int hammingWeight(uint32_t n) {
int res = 0;
for(int i = 0;i<32;i++){
if(n & 1 != 0) res++;
n >>= 1;
}
return res;
}
};
代码:
class Solution {
public:
int hammingWeight(uint32_t n) {
int res = 0;
while(n){
n &= (n-1);
res++;
}
return res;
}
};
力扣链接
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
代码:
class Solution {
public:
int singleNumber(vector<int>& nums) {
int res = 0;
for(int& num:nums){
res ^= num;
}
return res;
}
};
力扣链接
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
示例 1:
输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]
示例 2:
输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]
限制:
2 <= nums.length <= 10000
思路:
相同的数异或为0,不同的异或为1。0和任何数异或等于这个数本身。
所以,数组里面所有数异或 = 目标两个数异或 。 由于这两个数不同,所以异或结果必然不为0。
假设数组异或的二进制结果为10010,那么说明这两个数从右向左数第2位是不同的
那么可以根据数组里面所有数的第二位为0或者1将数组划分为2个。
这样做可以将目标数必然分散在不同的数组中,而且相同的数必然落在同一个数组中。
这两个数组里面的数各自进行异或,得到的结果就是答案
代码:
class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
int k = 0;//用于将所有的数异或起来
for(int num:nums){
k ^= num;
}
int mask = 1;
while((k&mask)==0){
mask <<= 1;
}
int res1 = 0, res2 = 0;
for(int num:nums){
if(num & mask){
res1 ^= num;
}else{
res2 ^= num;
}
}
return {res1,res2};
}
};
力扣链接
在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。
示例 1:
输入:nums = [3,4,3,3]
输出:4
示例 2:
输入:nums = [9,1,7,9,7,9,7]
输出:1
限制:
1 <= nums.length <= 10000
1 <= nums[i] < 2^31
思路:
如果一个数字出现3次,它的二进制每一位也出现的3次。如果把所有的出现三次的数字的二进制表示的每一位都分别加起来,那么每一位都能被3整除。 我们把数组中所有的数字的二进制表示的每一位都加起来。如果某一位能被3整除,那么这一位对只出现一次的那个数的这一肯定为0。如果某一位不能被3整除,那么只出现一次的那个数字的该位置一定为1.
代码:
class Solution {
public:
int singleNumber(vector<int>& nums) {
int res = 0;
for(int i = 0;i<32;i++){
int mask = 1 << i;
int cnt = 0;
for(int num:nums){
if(num & mask){
cnt++;
}
}
if(cnt % 3 != 0){
res |= mask;
}
}
return res;
}
};
力扣链接
给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。
如果存在一个整数 x 使得 n == 2x ,则认为 n 是 2 的幂次方。
示例 1:
输入:n = 1
输出:true
解释:20 = 1
示例 2:
输入:n = 16
输出:true
解释:24 = 16
示例 3:
输入:n = 3
输出:false
示例 4:
输入:n = 4
输出:true
示例 5:
输入:n = 5
输出:false
提示:
-231 <= n <= 231 - 1
进阶:你能够不使用循环/递归解决此问题吗?
class Solution {
public:
bool isPowerOfTwo(int n) {
return n>0 && (n & (n-1)) == 0;
}
};
力扣链接
给定一个整数,写一个函数来判断它是否是 3 的幂次方。如果是,返回 true ;否则,返回 false 。
整数 n 是 3 的幂次方需满足:存在整数 x 使得 n == 3x
示例 1:
输入:n = 27
输出:true
示例 2:
输入:n = 0
输出:false
示例 3:
输入:n = 9
输出:true
示例 4:
输入:n = 45
输出:false
提示:
-231 <= n <= 231 - 1
进阶:你能不使用循环或者递归来完成本题吗?
class Solution {
public:
bool isPowerOfThree(int n) {
while(n>0 && n%3==0){
n /= 3;
}
return n == 1;
}
};
给定一个整数,写一个函数来判断它是否是 4 的幂次方。如果是,返回 true ;否则,返回 false 。
整数 n 是 4 的幂次方需满足:存在整数 x 使得 n == 4x
示例 1:
输入:n = 16
输出:true
示例 2:
输入:n = 5
输出:false
示例 3:
输入:n = 1
输出:true
提示:
-231 <= n <= 231 - 1
进阶:你能不使用循环或者递归来完成本题吗?
class Solution {
public:
bool isPowerOfFour(int n) {
return n >0 && (n&(n-1))==0 && (n&0xAAAAAAAA) == 0;
}
};
力扣链接
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 “”。
示例 1:
输入:strs = [“flower”,“flow”,“flight”]
输出:“fl”
示例 2:
输入:strs = [“dog”,“racecar”,“car”]
输出:“”
解释:输入不存在公共前缀。
提示:
1 <= strs.length <= 200
0 <= strs[i].length <= 200
strs[i] 仅由小写英文字母组成
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
sort(strs.begin(),strs.end());
string st = strs.front(), en = strs.back();
int Len = min(st.size(), en.size());
string res;
for(int i = 0;i<Len;i++){
if(st[i] == en[i]){
res += st[i];
}else{
break;
}
}
return res;
}
};
力扣链接
给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。
示例 1:
输入: s = “abab”
输出: true
解释: 可由子串 “ab” 重复两次构成。
示例 2:
输入: s = “aba”
输出: false
示例 3:
输入: s = “abcabcabcabc”
输出: true
解释: 可由子串 “abc” 重复四次构成。 (或子串 “abcabc” 重复两次构成。)
提示:
1 <= s.length <= 104
s 由小写英文字母组成
class Solution {
public:
bool repeatedSubstringPattern(string s) {
//模拟
int n = s.size();
for(int len = 1;len<=n/2;len++){
if(n % len == 0){
bool match = true;
for(int i = len;i<n;i++){
if(s[i] != s[i-len]){
match = false;
break;
}
}
if(match) return true;
}
}
return false;
}
};
力扣链接
实现 strStr() 函数。
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。
说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符。
示例 1:
输入:haystack = “hello”, needle = “ll”
输出:2
示例 2:
输入:haystack = “aaaaa”, needle = “bba”
输出:-1
提示:
1 <= haystack.length, needle.length <= 104
haystack 和 needle 仅由小写英文字符组成
代码随想录
思路:
代码:
class Solution {
public:
void getNext(vector<int>& next, const string& s){
//初始化
//j指向前缀末尾位置,i指向后缀末尾位置。
int j = 0;
next[0] = 0;
for(int i = 1;i<s.size();i++){
//处理前后缀不相同的情况
while(j>0 && s[i] != s[j]){
j = next[j-1];
}
//处理前后缀相同的情况
if(s[i] == s[j]){
j++;
}
next[i] = j;
}
}
int strStr(string haystack, string needle) {
//KMP
if(needle.size() == 0) return 0;
int n = needle.size();
vector<int> next(n);
getNext(next,needle);
int j = 0;
for(int i = 0;i<haystack.size();i++){
while(j > 0 && haystack[i] != needle[j]){
j = next[j-1];
}
if(haystack[i] == needle[j]){
j++;
}
if(j == n){
return i - n + 1;
}
}
return -1;
}
};
class Solution {
public:
int strStr(string haystack, string needle) {
//BF
int index = 0;
int i = 0, j = 0;
while(i<haystack.length() && j<needle.length()){
if(haystack[i] == needle[j])
{
i++;
j++;
}
else
{
index++;
i = index;
j = 0;
}
}
if (j >= needle.length())
{
return index;
}
else{
return -1;
}
}
};