0
点赞
收藏
分享

微信扫一扫

随机链表的复制

慎壹 03-09 08:00 阅读 3
算法c++

1 算法题 :删除字符串中连续重复的字符

1.1 题目含义

题目要求删除字符串中连续重复的字符,即对于字符串中的每个字符,如果它与其前面的字符相同,则删除该字符。重复此过程,直到没有连续重复的字符为止。

1.2 示例

示例 1:
输入:“aabbbcccddeeeee”
输出:“abcde”
解释:从输入字符串开始,首先删除两个连续的 ‘a’,得到 “abbbcccddeeeee”。接着删除三个连续的 ‘b’,得到 “abcccddeeeee”。然后删除三个连续的 ‘c’,得到 “abddeeeee”。再删除四个连续的 ‘e’,最终得到 “abcde”

示例 2:
输入:“abcddeffg”
输出:“abcdfg”
解释:在这个例子中,没有连续重复的字符,所以直接返回原字符串。

示例 3:
输入:“aabbbcccaaa”
输出:“abca”
解释:首先删除两个连续的 ‘a’,得到 “abbbcccaaa”。然后删除三个连续的 ‘b’,得到 “abcccaa”。接着删除三个连续的 ‘c’,得到 “abcaa”。最后删除三个连续的 ‘a’,得到最终的结果 “abca”。

2 解题思路

解题思路如下:

为了删除字符串中连续重复的字符,可以使用双指针方法。其中一个指针 i 用来遍历字符串,而另一个指针 j 用来构建结果字符串。

步骤如下:

(1)初始化指针:
初始化两个指针 i 和 j,都指向字符串的起始位置。
初始化一个空字符串 result 用于存储最终结果。

(2)遍历字符串:
使用 i 指针遍历输入字符串的每个字符。
当 i 指针遇到与 i - 1 指针指向的字符不同的字符时,说明找到了一个新的字符序列的开始。

(3)处理连续重复字符:
当 i 指针遇到与 i - 1 指针指向的字符相同的字符时,说明遇到了连续重复字符,跳过这些字符。
重复此过程,直到 i 指针遇到与 i - 1 不同的字符为止。

(4)构建结果字符串:
当 i 指针遇到一个新的字符序列的开始时,将 j 指针所指的字符添加到 result 字符串中,并更新 j 指针的位置。
重复此过程,直到遍历完整个输入字符串。

(5)返回结果:
返回构建好的 result 字符串作为最终结果。

该算法时间复杂度是 O(n),其中 n 是输入字符串的长度。这是因为算法只遍历了一次输入字符串,并且在遍历过程中,对于每个字符只进行了常数时间的操作,如比较字符和更新结果字符串的指针。

算法中使用了两个指针 i 和 j,但它们都最多遍历了 n 个位置,其中 i 指针用于遍历输入字符串,而 j 指针用于构建结果字符串。由于每个字符的比较和赋值操作都是常数时间的,所以算法的总时间复杂度是线性的,即 O(n)。

空间复杂度方面,算法使用了一个额外的字符串 result 来存储结果。在最坏的情况下,即输入字符串中所有字符都是不同的,result 的长度将与输入字符串相同,即 n。因此,空间复杂度也是 O(n)。然而,如果输入字符串中有许多连续重复的字符,那么 result 的长度可能会远小于 n。但无论如何,空间复杂度的上界都是 O(n)。

3 算法实现代码

3.1 使用双指针方法

如下为算法实现代码:

#include <iostream>  
#include <string> 

class Solution
{
public:
	std::string removeDuplicateLetters(const std::string& str) {
		std::string result; 
		result.resize(str.length());
		int j = 0; // 结果字符串的指针  

		for (int i = 0; i < str.size(); ++i) {
			// 当遇到新字符时,将前一个字符添加到结果字符串中  
			if (i > 0 && str[i] != str[i - 1]) {
				result[j++] = str[i - 1];
			}
			// 处理最后一个字符的情况  
			if (i == str.size() - 1) {
				result[j++] = str[i];
			}
		}

		// 截断结果字符串,移除末尾的空字符  
		result.resize(j);

		return result;
	}
};

上面代码使用了 j 指针来记录结果字符串的当前位置,只有在遇到新字符序列的开始时,才将字符添加到结果字符串中。这样可以确保结果字符串中不包含连续重复的字符。注意:需要特别处理字符串的最后一个字符,因为在循环中它不会被检查是否与前一个字符相同。最后,使用 resize 方法来移除结果字符串末尾的空字符。

调用上面的算法,并得到输出:

int main()
{
	Solution ss;

	std::string input = "aabbbcccddeeeeeaa";
	std::string output = ss.removeDuplicateLetters(input);
	std::cout << "Input: " << input << std::endl;
	std::cout << "Output: " << output << std::endl;

	return 0;
}

上面代码的输出为:

Input: aabbbcccddeeeeeaa
Output: abcdea

3.2 使用栈实现的算法

栈在这里用于存储不连续的字符,每当遇到一个新的字符时,就将其压入栈中。如果下一个字符与栈顶字符相同,则弹出栈顶字符;如果不同,则继续压入栈中。这样,栈中始终存储的是不连续的字符。

如下为算法实现代码:

#include <iostream>  
#include <stack> 
#include <string> 

class Solution
{
public:
	std::string removeDuplicateLetters(const std::string& str) {
		std::stack<char> stk;
		std::string result;

		for (char c : str) {
			if (stk.empty() || stk.top() != c) {
				// 如果当前字符与栈顶字符不同,则将其压入栈中  
				stk.push(c);
			}
		}

		// 将栈中的字符弹出并构建成结果字符串  
		while (!stk.empty()) {
			result.push_back(stk.top());
			stk.pop();
		}

		// 由于栈是后进先出的,我们需要反转结果字符串  
		std::reverse(result.begin(), result.end());

		return result;
	}
};

这个算法的时间复杂度依然是 O(n),其中 n 是输入字符串的长度。算法遍历了输入字符串一次,对于每个字符,最多执行了一次压栈操作和一次弹栈操作,这些都是常数时间操作。因此,总的时间复杂度是线性的。

空间复杂度方面,最坏情况下,当输入字符串中所有字符都是不同的时,栈的大小将是 n。因此,空间复杂度的上界是 O(n)。在平均情况下,如果输入字符串中有许多连续重复的字符,那么栈的大小可能会远小于 n(而上面的第一种算法,则一开始就要创建一个大小为 n 的空间)。但是,我们依然可以说空间复杂度的上界是 O(n)。

4 测试用例

以下是针对上面算法的测试用例,基本覆盖了各种情况:

(1)基础测试用例

  • 输入:“aabbbcccddeeeee”
  • 预期输出:“abcde”
  • 解释:连续重复的字符被删除,保留不连续的字符。

(2)不包含连续重复字符的测试用例

  • 输入:“abcdefg”
  • 预期输出:“abcdefg”
  • 解释:输入字符串中没有连续重复字符,所以输出与输入相同。

(3)全为连续重复字符的测试用例

  • 输入:“aaaaa”
  • 预期输出:“a”
  • 解释:所有字符都是连续的,所以只保留一个。

(4)单个字符字符串的测试用例

  • 输入:“a”
  • 预期输出:“a”
  • 解释:单个字符不构成连续重复,因此输出与输入相同。

(5)空字符串的测试用例

  • 输入:“”
  • 预期输出:“”
  • 解释:空字符串不包含任何字符,因此输出为空。

(6)混合连续和非连续重复字符的测试用例

  • 输入:“aaabbbcccddeeeeefffabcggg”
  • 预期输出:“abcdefabcg”
  • 解释:删除连续重复的字符,保留非连续的字符。

(7)包含数字字符以及特殊字符的测试用例

  • 输入:“1122aaa3344bb&&&556677889900((”
  • 预期输出:“12a34b&567890(”
  • 解释:数字字符以及特殊字符的连续重复也应被正确删除。
举报

相关推荐

0 条评论