【基础算法总结】字符串

在这里插入图片描述

点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃

1.最长公共前缀

题目链接:14. 最长公共前缀

题目描述:

在这里插入图片描述

算法原理:

解法一:两两比较

在这里插入图片描述

解法二:统一比较

以第一个字符串下标0开始,每次比较所有字符串对应下标字符是否相等,不相等就结束了,并且如果字符串越界了也结束了。

在这里插入图片描述

class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {

        // 解法一: 两两比较
        // string ret = strs[0];
        // for(int i = 1; i < strs.size(); ++i)
        //     ret = FindCommon(ret,strs[i]);
        // return ret;

        // 解法二: 统一比较
        int m = strs[0].size(), n = strs.size();
        for(int i = 0; i < m; ++i)
        {
            char tmp = strs[0][i];
            for(int j = 1; j < n; ++j)
                if(i == strs[j].size() || tmp != strs[j][i]) 
                    return strs[0].substr(0,i);
        }
        return strs[0];  
    }

    // string FindCommon(string& s1, string& s2)
    // {
    //     int i = 0;
    //     while(i < min(s1.size(),s2.size()) && s1[i] == s2[i]) ++i;
    //     return s1.substr(0,i);
    // }
};

2.最长回文子串

题目链接:5. 最长回文子串

题目分析:

在这里插入图片描述

找字符串中最长回文字串。有奇数的回文子串,也有偶数的回文子串。
在这里插入图片描述
算法原理:

解法一:暴力枚举

把所有字串都找出来,判断是不是回文子串。但是时间复杂度比较高,找所有字串两次for循环 O(N ^ 2),判断是否是回文子串 O(N),总体时间复杂度是O(N ^ 3)。

解法二:中心扩展算法

中心扩展算法本质还是一个暴力枚举的策略,借助了回文串的特性来做枚举的。

固定一个中心点,从中心点开始,向两边扩展,看能扩展到什么位置。为什么能这样找,因为回文串的特性就是选择一个中点之后,左右两个字符是对称的。所以固定一个中点后,然后让left和right两个指针同时向两边扩展。移动要满足:left和right不越界并且left和right所指向的字符相等然后才能移动。一直移动到其中一个越界或者两个指针指向字符不相等就结束,那么两个指针中间这段就是以该中心点为中心的回文串。

在这里插入图片描述
上面这样枚举出来回文串的永远是奇数的,但是有可能最终结果是一个偶数的回文串。如何把偶数也枚举到呢?特别简单,固定这个中间点的时候可以先以这个i为中心的先left == right == i 然后left和right向两边扩展的找一下这个中心点奇数回文字串,找偶数回文字串可以 left == i , right = i +1 或者 left == i -1 ,right == i,然后left和right向两边扩展找一个这个中点的偶数回文子串

  1. 固定一个中心点
  2. 从中心点开始,向两边扩展,注意:奇数长度和偶数长度都需要考虑
class Solution {
public:
    string longestPalindrome(string s) {
        // 中心扩展算法
        int begin = 0, len = 0, n = s.size();
        for(int i = 0; i < n; ++i) // 依次枚举所有中点
        {       
            int left = i, right = i;
            // 先做一次奇数长度的扩展
            while(left >= 0 && right < n && s[left] == s[right])
            {
                --left;
                ++right;
            }
            if(right - left - 1 > len)
            {
                begin = left + 1;
                len = right - left - 1;
            }
            
            // 偶数长度的扩展
            left = i , right = i + 1;
            while(left >= 0 && right < n && s[left] == s[right]) 
            {
                --left;
                ++right;
            }

            if(right - left - 1 > len)
            {
                begin = left + 1;
                len = right - left - 1;
            }
        }
        return s.substr(begin,len);
    }
};

3.二进制求和

题目链接:67. 二进制求和

题目描述:

在这里插入图片描述

二进制求和这道题背后隐藏着高精度加法的算法,既然有高精度加法,那肯定还有高精度减法高精度乘法高精度除法

高精度是说这个数特别大,对这样一个数进行运算,就是高精度加法、减法、乘法、除法。不管是什么高精度,它的本质其实是一个模拟。

只要代码能力强 + 列竖式运算,都没有问题。

在这里插入图片描述

算法原理:

解法:模拟列竖式算法

加的时候可以用一个变量 t 记录进位。每次两个数和 t 相加,因为二进制逢2进1,所以要的是 t % 2 的结果,然后 t /= 2 就是这次的进位。重复这样的操作。如果加的时候某个数没有了就默认它为0,那 t 就不要加这个数了。

在这里插入图片描述

class Solution {
public:
    string addBinary(string a, string b) {
        string ret;
        int cur1 = a.size() - 1, cur2 = b.size() - 1, t = 0;
        while(cur1 >= 0 || cur2 >= 0 || t)
        {
            if(cur1 >= 0) t += a[cur1--] - '0';
            if(cur2 >= 0) t += b[cur2--] - '0';
            ret += t % 2 + '0';
            t /= 2;
        }
        reverse(ret.begin(),ret.end());
        return ret;
    }
};

4.字符串相乘

题目链接: 43. 字符串相乘

题目描述:

在这里插入图片描述

算法原理:

解法一:模拟列竖式运算

在这里插入图片描述
我们把列竖式过程,转化为代码。

因为要返回字符串,因此先搞个string ret = “0”; 大家会发现列竖式相乘过程其实本质就分两步,拿其中一个字符串中的每一位都和另一个字符串相乘,然后把乘完之后的结果累加起来。

我们可以在搞一个string tmp记录,拿其中一个字符串中的一位都和另一个字符串相乘之后的结果,比如1236=738,然后做一次 ret 和 tmp 两个字符串相加。同理1235、123*6都是这样做,当都算完之后ret就是最后结果。

所以我们总共就两步,第一步搞定每个字符都和字符串相乘,在搞定两个字符串相加就可以了。

在这里插入图片描述

但是这里有三个细节问题:

  1. 高位相乘的时候,要补上 “0”

比如说1235实际上应该是6150,补完之后在相加。那什么时候补?补几个呢?我们最好先把两个字符串逆序一下。其实做任何高精度的题时候首先先把字符串逆序, 上面那道题没逆序就是只是一个简单的加法,但是最好还是先逆序。逆序后会发现当1235的时候,补0的个数就是下标1的个数。1234 下标是2, 补2个0。那先补还是后补呢?其实应该先补,因为我们是逆序的。比如说1234 给到tmp应该是00294,tmp + ret ,ret也是逆序的,当把所有tmp加完 tmp 结果应该是 88065,最后我们把ret逆序一下就得到56088

在这里插入图片描述

  1. 处理前导 “0”

比如一个字符串是123,另一个字符串是0,乘之后, tmp + ret就是000,但是我们只需要一个0就好了。因为ret是逆序的,所以ret.pop_back(),留下一个0。

在这里插入图片描述
3. 注意计算结果的顺序

因为我们都是逆序操作的,所以ret最后结果一定要逆序。

解法二:对解法一做一下优化 —> 无进位相乘然后相加,最好处理进位。

先模拟一下。

在这里插入图片描述
发现结果也是正确的,上面是相乘之后先处理进位然后相加,我们这里是相乘后先相加在处理进位。

我们依旧先将两个字符串逆序,然后存无进位相乘的结果我们就不能用字符串了。之前可以用字符串存是因为字符串里单个字符正好对应一个一个数,但是我们这里存的时候不能拿一个string来存相乘的结果,因为你并不知道相乘之后是一个字符还是两个字符。都有可能。
在这里插入图片描述

所以这里我们搞一个int tmp数组,这个数组大小特别好计算。设字符串长度大小一个为m、一个为n。最终想存无进位相乘相加的结果,数组大小 m + n -1 就够了。

在这里插入图片描述

然后的时候也特别好放,就是相乘之后的结果放在数组下标为两个字符之和的位置。然后乘完之和累加。
在这里插入图片描述

然后处理进位也特别简单,就是模拟两数相加的过程。t 是进位 ,t += 18,
ret += (t % 10) +‘0’,t /= 10,把数组中所有数都处理好,ret就是结果,别忘了先处理前导零的情况,然后因为是逆序的,所以ret最后逆序一下。

class Solution {
public:
    string multiply(string num1, string num2) {
        //解法二: 无进制位相乘后相加,然后处理进制位

        // 1.准备工作
        int m = num1.size(), n = num2.size();
        reverse(num1.begin(), num1.end());
        reverse(num2.begin(), num2.end());
        int *tmp = new int[m + n - 1]{0};

        // 1.无进制位相乘后相加
        for(int i = 0; i < m; ++i)
            if(num1[i] ==  '0') continue;
            else
                for(int j = 0; j < n; ++j)
                    tmp[i + j] += (num1[i] - '0') * (num2[j] -'0');

        // 2.处理进位
        string ret;
        int t = 0, i = 0;
        while(i < m + n -1 || t)
        {
            if(i < m + n - 1) t += tmp[i++];
            ret += t % 10 + '0';
            t /= 10;
        }

        // 3.处理前导零  如 123 * 0 = 000  -> 0 保留一个0即可
        while(ret.size() > 1 && ret.back() == '0') ret.pop_back();
        reverse(ret.begin(), ret.end());
        return ret;
    }
};
  • 38
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 50
    评论
评论 50
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值