【基础算法总结】字符串
点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃
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向两边扩展找一个这个中点的偶数回文子串
- 固定一个中心点
- 从中心点开始,向两边扩展,注意:奇数长度和偶数长度都需要考虑
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就是最后结果。
所以我们总共就两步,第一步搞定每个字符都和字符串相乘,在搞定两个字符串相加就可以了。
但是这里有三个细节问题:
- 高位相乘的时候,要补上 “0”
比如说1235实际上应该是6150,补完之后在相加。那什么时候补?补几个呢?我们最好先把两个字符串逆序一下。其实做任何高精度的题时候首先先把字符串逆序, 上面那道题没逆序就是只是一个简单的加法,但是最好还是先逆序。逆序后会发现当1235的时候,补0的个数就是下标1的个数。1234 下标是2, 补2个0。那先补还是后补呢?其实应该先补,因为我们是逆序的。比如说1234 给到tmp应该是00294,tmp + ret ,ret也是逆序的,当把所有tmp加完 tmp 结果应该是 88065,最后我们把ret逆序一下就得到56088
- 处理前导 “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;
}
};