[力扣刷题总结](数学和字符串篇)

文章目录

  • 7. 整数反转
    • 解法1:数学
  • 9. 回文数
    • 解法1:普通解法:整数转为字符串
    • 解法2:反转一半数字
  • 8. 字符串转换整数 (atoi)
    • 解法1:有限状态机
  • 12. 整数转罗马数字
    • 解法1:字符串+哈希表
  • 相似题目:13. 罗马数字转整数
    • 解法1:字符串+哈希表
  • 38. 外观数列
    • 解法1:模拟+字符串
  • 151. 翻转字符串里的单词
    • 解法1:字符串+双指针
  • 剑指 Offer 58 - II. 左旋转字符串
    • 解法1:字符串
  • 相似题目:189. 轮转数组
    • 解法1:字符串
  • 179. 最大数
    • 解法1:贪心+排序+字符串
  • 2038. 如果相邻两个颜色均相同则删除当前颜色
    • 解法1:贪心
  • 50. Pow(x, n)
    • 解法1:分治-递归
  • 69. x 的平方根
    • 解法1:二分查找
    • 解法2:牛顿迭代法
  • 43. 字符串相乘
    • 解法1:模拟竖式乘法
  • 相似题目:415. 字符串相加
    • 解法1:模拟
  • 394. 字符串解码
    • 解法1:辅助栈
  • 470. 用 Rand7() 实现 Rand10()
    • 解法1:万能构造法:独立随机事件+古典概型
  • 400. 第 N 位数字
    • 解法1:直接模拟
  • ~~~~~~位运算~~~~~~
  • 693. 交替位二进制数
    • 解法1:位运算
  • 相似题目:191. 位1的个数
    • 解法1:循环检查二进制位
    • 解法2:位运算优化
  • 136. 只出现一次的数字
    • 解法1:位运算
  • 相似题目:剑指 Offer 56 - I. 数组中数字出现的次数
    • 解法1:位运算
  • 剑指 Offer 56 - II. 数组中数字出现的次数 II
    • 解法1:位运算
  • 231. 2 的幂
    • 解法1:
  • 相似题目:326. 3 的幂
    • 解法1:递归
  • 相似题目:342. 4的幂
    • 解法1:位运算
  • ~~~~~~字符串~~~~~~
  • 14. 最长公共前缀
    • 解法1:字符串
  • 459. 重复的子字符串
    • 解法1:模拟+穷举
  • 28. 实现 strStr()
    • 解法1:KMP
    • 解法2:暴力解法


7. 整数反转

力扣链接
给你一个 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

解法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)。

9. 回文数

力扣链接
给你一个整数 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

进阶:你能不将整数转为字符串来解决这个问题吗?

解法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;
    }
};

解法2:反转一半数字

思路:
[力扣刷题总结](数学和字符串篇)_第1张图片
[力扣刷题总结](数学和字符串篇)_第2张图片
[力扣刷题总结](数学和字符串篇)_第3张图片

代码:

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)。我们只需要常数空间存放若干变量。

8. 字符串转换整数 (atoi)

力扣链接
请你来实现一个 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 。
返回整数作为最终结果。
注意:

本题中的空白字符只包括空格字符 ’ ’ 。
除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。
[力扣刷题总结](数学和字符串篇)_第4张图片
[力扣刷题总结](数学和字符串篇)_第5张图片
[力扣刷题总结](数学和字符串篇)_第6张图片

解法1:有限状态机

思路:
[力扣刷题总结](数学和字符串篇)_第7张图片
[力扣刷题总结](数学和字符串篇)_第8张图片
[力扣刷题总结](数学和字符串篇)_第9张图片
[力扣刷题总结](数学和字符串篇)_第10张图片

代码:

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)。自动机的状态只需要常数空间存储。

12. 整数转罗马数字

力扣链接
罗马数字包含以下七种字符: 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

解法1:字符串+哈希表

思路:
罗马数字由 7 个不同的单字母符号组成,每个符号对应一个具体的数值。此外,减法规则(如问题描述中所述)给出了额外的 6 个复合符号。这给了我们总共 13 个独特的符号(每个符号由 1 个或 2 个字母组成),如下图所示。

[力扣刷题总结](数学和字符串篇)_第11张图片

代码:

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)。

相似题目:13. 罗马数字转整数

力扣链接
罗马数字包含以下七种字符: 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 。

解法1:字符串+哈希表

思路:

[力扣刷题总结](数学和字符串篇)_第12张图片

代码:

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)。

38. 外观数列

力扣链接
给定一个正整数 n ,输出外观数列的第 n 项。

「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。

你可以将其视作是由递归公式定义的数字字符串序列:

countAndSay(1) = “1”
countAndSay(n) 是对 countAndSay(n-1) 的描述,然后转换成另一个数字字符串。
前五项如下:

  1. 1
    
  2. 11
    
  3. 21
    
  4. 1211
    
  5. 111221
    

第一项是数字 1
描述前一项,这个数是 1 即 “ 一 个 1 ”,记作 “11”
描述前一项,这个数是 11 即 “ 二 个 1 ” ,记作 “21”
描述前一项,这个数是 21 即 “ 一 个 2 + 一 个 1 ” ,记作 “1211”
描述前一项,这个数是 1211 即 “ 一 个 1 + 一 个 2 + 二 个 1 ” ,记作 “111221”
要 描述 一个数字字符串,首先要将字符串分割为 最小 数量的组,每个组都由连续的最多 相同字符 组成。然后对于每个组,先描述字符的数量,然后描述字符,形成一个描述组。要将描述转换为数字字符串,先将每组中的字符数量用数字替换,再将所有描述组连接起来。

例如,数字字符串 “3322251” 的描述如下图:

[力扣刷题总结](数学和字符串篇)_第13张图片

示例 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

解法1:模拟+字符串

思路:
一个朴素的想法是:根据题意进行模拟,从起始条件 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)

151. 翻转字符串里的单词

力扣链接
给你一个字符串 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:字符串+双指针

思路:

(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) 的额外空间来存放若干变量。

剑指 Offer 58 - II. 左旋转字符串

力扣链接
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。

示例 1:

输入: s = “abcdefg”, k = 2
输出: “cdefgab”

示例 2:

输入: s = “lrloseumgh”, k = 6
输出: “umghlrlose”

限制:

1 <= k < s.length <= 10000

解法1:字符串

思路:
为了让本题更有意义,提升一下本题难度:**不能申请额外空间,只能在本串上操作。**不能使用额外空间的话,模拟在本串操作要实现左旋转字符串的功能还是有点困难的。

那么我们可以想一下上一题目字符串:花式反转还不够! (opens new window)中讲过,使用整体反转+局部反转就可以实现,反转单词顺序的目的。

这道题目也非常类似,依然可以通过局部反转+整体反转 达到左旋转的目的

具体步骤为:

反转区间为前n的子串
反转区间为n到末尾的子串
反转整个字符串

最后就可以得到左旋n的目的,而不用定义新的字符串,完全在本串上操作。

例如 :示例1中 输入:字符串abcdefg,n=2

如图:
[力扣刷题总结](数学和字符串篇)_第14张图片
最终得到左旋2个单元的字符串:cdefgab

思路明确之后,那么代码实现就很简单了

代码:

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;
    }
};

相似题目:189. 轮转数组

力扣链接
给你一个数组,将数组中的元素向右轮转 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) 的 原地 算法解决这个问题吗?

解法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());
    }
};

179. 最大数

力扣链接

给定一组非负整数 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

解法1:贪心+排序+字符串

思路:

今天的题目,让我们将一组数字重新组合,构成一个最大的整数。由于构成的整数可能非常大,所以返回结果需要字符串格式。

输入数组的长度会达到 100,如果你想用 DFS 找出所有可以拼接的结果然后求最大,那么就走偏了,一定会超时,而且会超出空间限制。

我们分析一下规律:

  1. 当 nums = [10,2] 时,结果是 210。其实我们就是在比较 [10,2] 这两个数字能组合成的 210 和 102 哪个数字更大,显然 210 更大,所以需要把 2 放在前面。

  2. 为了避免用 int 型或者 long 型越界,所以我们需要把数字先转成字符串。 然后可以把待比较的两个数字 x,y 组合成两个新的数字 string(x) + string(y) 和 string(y) + string(x) ,比较一下哪种组合构成的数字更大。

你可能会有疑问:如果拼接得到的字符串结果更大的话,那么原本的整型的数字拼接结果也一定更大吗?比如 “210” > “102”,那么一定能得到 210 > 102 么?

答案是肯定的:首先拼接成的两个字符串一定是等长的。 等长的字符串在比较的时候,是按照字符串的各个字符从前向后逐个比较的,所以相当于先比较了百分位,然后比较十分位,最后比较个位。 所以在字符串等长的情况下,字符串大,那么对应的整型也更大。但两个不等长的字符串就没有这个结论了, 比如 “2” > “10”,但是 2 < 10。

综上,我们按照下面的步骤:

  1. 先把 nums 中的所有数字转字符串,形成字符串数组 nums_str;
  2. 比较两个字符串 x,y 拼接的结果 x + y 和 y + x 哪个更大,从而确定 x 和 y 谁排在前面;将 nums_str 降序排序;
  3. 把整个数组排序的结果拼接成一个字符串,并返回。

本题有个坑:如果输入的 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;
    } 
};

2038. 如果相邻两个颜色均相同则删除当前颜色

力扣链接
总共有 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’

解法1:贪心

代码:

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;
    }
};

50. Pow(x, n)

实现 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

解法1:分治-递归

思路:
[力扣刷题总结](数学和字符串篇)_第15张图片[力扣刷题总结](数学和字符串篇)_第16张图片[力扣刷题总结](数学和字符串篇)_第17张图片[力扣刷题总结](数学和字符串篇)_第18张图片
代码:

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);
    }
};

[力扣刷题总结](数学和字符串篇)_第19张图片

69. x 的平方根

力扣链接
给你一个非负整数 x ,计算并返回 x 的 算术平方根 。

由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。

注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。

示例 1:

输入:x = 4
输出:2
示例 2:

输入:x = 8
输出:2
解释:8 的算术平方根是 2.82842…, 由于返回类型是整数,小数部分将被舍去。

提示:

0 <= x <= 231 - 1

解法1:二分查找

[力扣刷题总结](数学和字符串篇)_第20张图片

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;
    }
};

[力扣刷题总结](数学和字符串篇)_第21张图片

解法2:牛顿迭代法

[力扣刷题总结](数学和字符串篇)_第22张图片[力扣刷题总结](数学和字符串篇)_第23张图片[力扣刷题总结](数学和字符串篇)_第24张图片[力扣刷题总结](数学和字符串篇)_第25张图片

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);
    }
};

[力扣刷题总结](数学和字符串篇)_第26张图片

43. 字符串相乘

力扣链接
给定两个以字符串形式表示的非负整数 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本身。

解法1:模拟竖式乘法

[力扣刷题总结](数学和字符串篇)_第27张图片

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";
    }
};

[力扣刷题总结](数学和字符串篇)_第28张图片

相似题目:415. 字符串相加

力扣链接
给定两个字符串形式的非负整数 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 都不包含任何前导零

解法1:模拟

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;
    }
};

[力扣刷题总结](数学和字符串篇)_第29张图片

394. 字符串解码

力扣链接
给定一个经过编码的字符串,返回它解码后的字符串。

编码规则为: 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]

解法1:辅助栈

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;
    }
};

470. 用 Rand7() 实现 Rand10()

力扣链接
给定方法 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() ?

解法1:万能构造法:独立随机事件+古典概型

参考

rand7() 构造 rand10()

  1. 构造 22 次采样,分别有 2和 5 种结果,组合起来便有 10 种概率相同的结果。
  2. 把这 10 种结果映射到 [1,10]即可。
    第一步具体要如何构造采样是自由的,比如 rand7()拒绝 7,然后对 [1,6] 采样,把奇数和偶数作为 2种结果,这 2 种结果的概率均为 0.5 。rand7() 拒绝 6,7 ,然后对[1,5] 采样,有 5 种结果,每种概率均为 0.2 。

[力扣刷题总结](数学和字符串篇)_第30张图片

// 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;
    }
};

在这里插入图片描述

400. 第 N 位数字

力扣链接
给你一个整数 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

解法1:直接模拟

[力扣刷题总结](数学和字符串篇)_第31张图片

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';
    }
};

[力扣刷题总结](数学和字符串篇)_第32张图片

位运算

693. 交替位二进制数

力扣链接
给定一个正整数,检查它的二进制表示是否总是 0、1 交替出现:换句话说,就是二进制表示中相邻两位的数字永不相同。

示例 1:

输入:n = 5
输出:true
解释:5 的二进制表示是:101
示例 2:

输入:n = 7
输出:false
解释:7 的二进制表示是:111.
示例 3:

输入:n = 11
输出:false
解释:11 的二进制表示是:1011.

提示:

1 <= n <= 231 - 1

解法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;
    }
};

相似题目:191. 位1的个数

力扣链接
编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。

提示:

请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。

示例 1:

输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 ‘1’。
示例 2:

输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 ‘1’。
示例 3:

输入:11111111111111111111111111111101
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 ‘1’。

提示:

输入必须是长度为 32 的 二进制串 。

进阶:

如果多次调用这个函数,你将如何优化你的算法?

解法1:循环检查二进制位

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;
    }
};

[力扣刷题总结](数学和字符串篇)_第33张图片

解法2:位运算优化

思路:
[力扣刷题总结](数学和字符串篇)_第34张图片

代码:

class Solution {
public:
    int hammingWeight(uint32_t n) {
        int res = 0;
        while(n){
            n &= (n-1);
            res++;
        }
        return res;
    }
};

[力扣刷题总结](数学和字符串篇)_第35张图片

136. 只出现一次的数字

力扣链接
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,1]
输出: 1
示例 2:

输入: [4,1,2,1,2]
输出: 4

解法1:位运算

思路:
[力扣刷题总结](数学和字符串篇)_第36张图片

代码:

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int res = 0;
        for(int& num:nums){
            res ^= num;
        }
        return res;
    }
};

相似题目:剑指 Offer 56 - I. 数组中数字出现的次数

力扣链接
一个整型数组 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

解法1:位运算

思路:
相同的数异或为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};
    }
};

剑指 Offer 56 - II. 数组中数字出现的次数 II

力扣链接
在一个数组 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

解法1:位运算

思路:

如果一个数字出现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;
    }
};

[力扣刷题总结](数学和字符串篇)_第37张图片

231. 2 的幂

力扣链接
给你一个整数 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

进阶:你能够不使用循环/递归解决此问题吗?

解法1:

[力扣刷题总结](数学和字符串篇)_第38张图片

class Solution {
public:
    bool isPowerOfTwo(int n) {
        return n>0 && (n & (n-1)) == 0;
    }
};

[力扣刷题总结](数学和字符串篇)_第39张图片

相似题目:326. 3 的幂

力扣链接
给定一个整数,写一个函数来判断它是否是 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

进阶:你能不使用循环或者递归来完成本题吗?

解法1:递归

class Solution {
public:
    bool isPowerOfThree(int n) {
        while(n>0 && n%3==0){
            n /= 3;
        }
        return n == 1;
    }
};

[力扣刷题总结](数学和字符串篇)_第40张图片

相似题目:342. 4的幂

给定一个整数,写一个函数来判断它是否是 4 的幂次方。如果是,返回 true ;否则,返回 false 。

整数 n 是 4 的幂次方需满足:存在整数 x 使得 n == 4x

示例 1:

输入:n = 16
输出:true
示例 2:

输入:n = 5
输出:false
示例 3:

输入:n = 1
输出:true

提示:

-231 <= n <= 231 - 1

进阶:你能不使用循环或者递归来完成本题吗?

解法1:位运算

[力扣刷题总结](数学和字符串篇)_第41张图片

class Solution {
public:
    bool isPowerOfFour(int n) {
        return n >0 && (n&(n-1))==0 && (n&0xAAAAAAAA) == 0;
    }
};

字符串

14. 最长公共前缀

力扣链接
编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 “”。

示例 1:

输入:strs = [“flower”,“flow”,“flight”]
输出:“fl”
示例 2:

输入:strs = [“dog”,“racecar”,“car”]
输出:“”
解释:输入不存在公共前缀。

提示:

1 <= strs.length <= 200
0 <= strs[i].length <= 200
strs[i] 仅由小写英文字母组成

解法1:字符串

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;
    }
};

459. 重复的子字符串

力扣链接
给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。

示例 1:

输入: s = “abab”
输出: true
解释: 可由子串 “ab” 重复两次构成。
示例 2:

输入: s = “aba”
输出: false
示例 3:

输入: s = “abcabcabcabc”
输出: true
解释: 可由子串 “abc” 重复四次构成。 (或子串 “abcabc” 重复两次构成。)

提示:

1 <= s.length <= 104
s 由小写英文字母组成

解法1:模拟+穷举

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;
    }
};

28. 实现 strStr()

力扣链接
实现 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 仅由小写英文字符组成

解法1:KMP

代码随想录
思路:

KMP
[力扣刷题总结](数学和字符串篇)_第42张图片

[力扣刷题总结](数学和字符串篇)_第43张图片
[力扣刷题总结](数学和字符串篇)_第44张图片
[力扣刷题总结](数学和字符串篇)_第45张图片

代码:

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;
    }
};

[力扣刷题总结](数学和字符串篇)_第46张图片

解法2:暴力解法

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;
        }
    }
};

[力扣刷题总结](数学和字符串篇)_第47张图片

你可能感兴趣的:(数据结构与算法基础,leetcode,算法,动态规划)