一、题目描述
原题传送门
二、思路分析
首先我们来分析一下解决本题所需要的思路
- 题目的意思很简单,就是统计原本的字符串中的每个字符出现的次数,然后以【字符,出现的次数】这样的结构来字符串,以起到一个压缩的效果,那么对于这样的结构,详细很多力友都会想到
map
这个键值对的结构,但是认真查看题目本身的话却发现我们不可以用这种结构
输入:“aabcccccaaa”
输出:“a2b1c5a3”
- 我们以第一个为例,从左往右分别是2个a、1个b、5个c和3个a,此时若我们通过map去做遍历的话,开头的【a】和结尾的【a】就会合并在一起变为5个a,那么最后在拼接字符串的时候就会出现问题,所以
map
的这种结构我们是不能使用
unordered_map<char, int> count
for(char c : S)
{
count[c]++;
}
接下去就来看看下面的两种做法👇
Way1 : 遍历统计
- 首先是第一种方法,既然题目给到了一个字符串,那我们就去遍历它并进行一个统计,那既然是统计某个字符出现了多少次的话,就需要有一个字符变量去记录,这里我们拿
ch
首先保存下第一个字符,后面遍历的时候再做更新
char ch = S[0];
- 当然,我们还需要一个计数器
cnt
- 接下去这段逻辑就是在遍历这个字符串的同时去统计里面的每个字符出现了多少次,如果当前所遍历的字符和
ch
是相同的话,计数器cnt
就去做一个累加,如果不相同的话,我们就可以去拼接字符串了,ch
即为当前所需要统计的字符,cnt
即为这个字符所出现的次数,不过我们要使用到C++里的一个库函数叫做 to_string ,不清楚的力友可以先去了解一下 - 当这个字符拼接完后,我们就要去统计下一个字符了,这个时候就要更新【ch】和【cnt】了
string compressStr = "";
for(int i = 1;i < sz; ++i)
{
if(S[i] == ch){
cnt++; // 如果当前所遍历到的字符与需统计字符相同的话,则计数器 + 1
}else{
compressStr += ch + to_string(cnt);
ch = S[i];
cnt = 1;
}
}
- 最后呢,在遍历结束之后我们还需要在去做一个拼接,因此当最后遍历最后跳出循环并没有把最后一个字符给拼接上去
// 最后在遍历结束后添加最后一个字符的统计结果
compressStr += ch + to_string(cnt);
- 当字符拼接完后,就将其返回,但是题目中说到如果压缩之后的字符串长度比原字符串还长的话,就返回原先的串
return compressStr.size() >= sz ? S : compressStr;
- 不过这里还有隐藏的一个测试案例,我们应该要考虑到压缩后的字符串和原串相等的情况,也是需要返回原串的
代码展示:
class Solution {
public:
string compressString(string S) {
int sz = S.size();
if(sz == 0)
return S; // 如果是空串的话,则返回自己本身
string compressStr = "";
int cnt = 1; // 统计每一个字符个数
char ch = S[0]; // 记录当前所要统计的字符
// 循环直接从1开始即可,第一个字符一定算一个
for(int i = 1;i < sz; ++i)
{
if(S[i] == ch){
cnt++; // 如果当前所遍历到的字符与需统计字符相同的话,则计数器 + 1
}else{
compressStr += ch + to_string(cnt);
ch = S[i];
cnt = 1;
}
}
// 最后在遍历结束后添加最后一个字符的统计结果
compressStr += ch + to_string(cnt);
// 三目运算符:若是压缩后的字符串长度 >= 原字符串的长度,则返回原串
return compressStr.size() >= sz ? S : compressStr;
}
};
最后展示一下AC后的结果
Way2 : 双指针
接下去要介绍的就是另一种解法,此解法来自 K神 很是巧妙,力友可以学习一下这种解法
图解分析
总体思路概述:
- 这种方法很巧妙地利用了双指针,首先让指针
i
和j
指向当前字符串的首位,然后让指针j
不断地先后遍历,比较 s[i] 和 s[j] 的的字符是否相同,若是相同的话则让j
继续后移,直到二者不相同为止。 - 此时我们便去计算当前的这个字符出现了多少次,使用【指针 - 指针】的办法算出来。然后再去统计下一个字符的位置,此时让
i
移动到j
的位置来即可,因为二者不相同后j
一定指向了一个新的字符 - 然后继续重复上面的逻辑就可以了,直到这个字符串遍历完之后也就压缩完了
然后我们就根据 示例1 来分布细述一下
- 首先双指针都指向字符串的首位置
- 然后因为【a = a】,所以
j++
,i
不动
- 继续还是一样,【a = a】,
j++
- 那么接下来【a != b】,遇到不相等了,此时我们就需要去统计当前这个字符所出现的此处,使用
j - i
就可以计算得到,那么此时就可以将这结果追加到压缩后的字符串中了。接下去便是更新指针i
的位置到j
这里来,进行下一个字符串个数的统计
- 接下去就开始统计字符b出现的个数了,但是呢在比较了一次够就不相等了,于是立马就得出这个字符只出现了一次,步骤和上述一样
- 接下去就是字符【c】了,利用
j - i
可以算出其出现了5次,继续拼接到压缩串compressStr中
- 最后当这个串遍历完之后,因为没有再进入循环了,所以我们在最后还要拼接一次
- 最后的话当我们要返回结果的时候还需要比较一下压缩后的串和原串的长度,只有当压缩后的串来得短的时候才可返回,否则一律返回原串
代码展示:
class Solution {
public:
string compressString(string S) {
if(S.size() == 0) return S;
int i = 0, j = 0;
string compressStr = "";
while(j < S.size())
{
// 持续比较双指针上的位置,直到不相同为为止
while(S[i] == S[j]){
j++;
}
compressStr += S[i];
compressStr += to_string(j - i);
i = j; // i换位到新字符的位置
}
return compressStr.size() >= S.size() ? S : compressStr;
}
};
最后从提交的结果就可以看出效率提升了不少 ↑
认真看完上面这个例子后,其他的示例相信你一定也能推敲出来🌹