【算法】活用双指针完成--字符串相减(双指针,图例详解!)-二、字符串相减 

时间:2024-03-31 16:57:24

✨ 题目描述

 题目描述:给定两个字符串形式的非负整数 num1 和num2计算它们的差

注意:

  1. num1 和num2 都只会包含数字 0-9

  2. num1 和num2 都不包含任何前导零

  3. 你不能使用任何內建 BigInteger 库

✨ 题目分析 

题目分析:  

  • 我们先假设给定的不是字符串形式的数字,而是正常的非负整数,则两数相减遵循正常的减法运算,个位数与个位数相减,十位数与十位数相减,如果该位计算结果<0,则向前借位。 
  •  既然我们对加法的运算非常熟悉,代码也非常好写,那我们现在的任务就是将字符串形式的数字转换为正常的数字进行算术运算。
  • 那么我们应该怎样取字符串中的每一位数字呢?我们定义两个指针,分别指向两个字符串的尾,然后取出该位置的字符,并将其转换为整数形式参与运算,并将结果再次转换为字符串形式,依次进行计算。 (双指针算法)

上图中就是我们计算字符串相减的一个大概思路,但是这里需要注意几点情况: 

  • a:两个非负整数相减的结果可能为负;
  •  b:注意边界问题

 针对情况a: 

  • 因此,首先比较两个数的大小。

 如何比较两个大数的大小呢?

  • 由于是大数,肯定不能直接转成int比较。
  • 我们可以比较两个字符串的长度。
  • 长度更长的字符串,数一定更大;当长度一样的就去比较字典序。
// 防止字符串是 小减大  
// 正常来说 是 a-b 且 a>b
bool isLess(string a, string b)
{
	if (a.size() == b.size())
	{
		//当长度一样的就去比较字典序。
		return a < b;
	}
	// 长度更长的字符串,数一定更大
	return a.size() < b.size();
}
  • 如代码所示,当小减大时,需将两个参数调换一下位置执行减法,在结果前填上负号即可
  • 注意:结果为0时不加负号。
// 保证 大数减法 正常进行
string substrings(string num1, string num2)
{
	string res;

	// 发现 num1 < num2
	if (isLess(num1, num2))
	{
		// 转换顺序 ,加上 负号
		res = sub(num2, num1);
		// 如果不等于 0 将其反过来即可 
		if (res != "0")
		{
			res.insert(0, "-");
		}
	}
	else  // num1 > num2
	{
		res = sub(num1, num2);
	}
	return res;
}

 大体框架写完了,接下来根据上述的图例来,实现关键的减法函数

//  减法 
string sub(string a, string b)
{
	string s;
	// 借位
	int next = 0;
	int len1 = a.size() - 1 , len2 = b.size() - 1;

	// 两个字符串都从 最后一位开始减
	while (len1 >= 0 || len2 >= 0)
	{
		int val1 = 0;
		if (len1 >= 0)
		{
			val1 = a[len1--] - '0';
		}
		int val2 = 0;
		if (len2 >= 0)
		{
			val2 = b[len2--] - '0';
		}

		// 每次都假设借位
		// %10 可以满足 借位和不借位两种情况
		int ret = (val1 - val2 - next + 10) % 10;

		// 判断是否借位
		if ((val1 - val2 - next) < 0)
		{
			next = 1;
		}
		else
		{
			next = 0;
		}

		s += ret + '0';
	}
	reverse(s.begin(), s.end());
	// 删除前导 0 ,注意边界是 s.size()-1  防止当s为 "0000"
	int pos;
	for (pos = 0; pos < s.size() - 1; pos++)
	{
		if (s[pos] != '0')
		{
			break;
		}
	}
	return s.substr(pos);
}

 针对问题b:边界问题

     例如,当121-120=001,需要将前面的0删除,得到最终结果1。注意121-121=000这种情况,不要把所有0都删了!

 ✨完整代码展示

 【C++】完整代码如下:

// 防止字符串是 小减大  
// 正常来说 是 a-b 且 a>b
bool isLess(string a, string b)
{
	if (a.size() == b.size())
	{
		//当长度一样的就去比较字典序。
		return a < b;
	}
	// 长度更长的字符串,数一定更大
	return a.size() < b.size();
}

//  减法 
string sub(string a, string b)
{
	string s;
	// 借位
	int next = 0;
	int len1 = a.size() - 1 , len2 = b.size() - 1;

	// 两个字符串都从 最后一位开始减
	while (len1 >= 0 || len2 >= 0)
	{
		int val1 = 0;
		if (len1 >= 0)
		{
			val1 = a[len1--] - '0';
		}
		int val2 = 0;
		if (len2 >= 0)
		{
			val2 = b[len2--] - '0';
		}

		// 每次都假设借位
		// %10 可以满足 借位和不借位两种情况
		int ret = (val1 - val2 - next + 10) % 10;

		// 判断是否借位
		if ((val1 - val2 - next) < 0)
		{
			next = 1;
		}
		else
		{
			next = 0;
		}

		s += ret + '0';
	}
	reverse(s.begin(), s.end());
	// 删除前导 0 ,注意边界是 s.size()-1  防止当s为 "0000"
	int pos;
	for (pos = 0; pos < s.size() - 1; pos++)
	{
		if (s[pos] != '0')
		{
			break;
		}
	}
	return s.substr(pos);
}

// 保证 大数减法 正常进行
string substrings(string num1, string num2)
{
	string res;

	// 发现 num1 < num2
	if (isLess(num1, num2))
	{
		// 转换顺序 ,加上 负号
		res = sub(num2, num1);
		// 如果不等于 0 将其反过来即可 
		if (res != "0")
		{
			res.insert(0, "-");
		}
	}
	else  // num1 > num2
	{
		res = sub(num1, num2);
	}
	return res;
}

int main()
{
	string a, b, c;
	cin >> a >> b;
	cout << substrings(a, b) << endl;
	return 0;
}