错误类型
程序出现问题的解决步骤:
- 观察现象确定“问题类型”(三种类型)
- 使用不同工具“定位问题”
- 使用错误类型的解决方式“解决问题”
-
第一类错误:编译器提示的错误(语法错误)
[现象1] undefined reference to `WinMain’
[现象2] expected ‘,’ or ‘;’ before ‘return’
[现象3] stray ‘\243’ in program
[现象n] 。。。
[***解决方式1]看编译器的提示
[**解决方式2]向群里、老师、学长求助, 可以百度 -
第二类错误:操作系统提示的错误(内存错误)
[概念1] 硬件层 <[管理]< 系统层 >[管理]> 应用程序(APP,运行中的代码)
[概念2] 每个应用程序都有自己的内存空间(类比:系统-政府,应用-人)
[概念3] 当应用访问到不属于自己的空间时, 系统会[干掉]你的程序
[现象] 程序正常 - 终端返回0,程序不正常 - 终端返回非0值
[***解决方式1] 用“输出”定位问题在哪一行(指针/数组)
[*解决方式2] IDE >> DEBUG模式 -
第三类错误:自己给自己的错误(逻辑错误)
[现象] 程序的运行结果不对
解决方式1 单元测试-用“输出”确定每一步的正确性(代码问题)
解决方式2 不看代码 - 重新思考程序设计的逻辑问题(设计问题)
思路分析
- 数据存储
- 用C/C++字符串或数组存储
- 程序设计
- 第一层循环定位a的位置i
- 第二层循环定位b的位置j
- 判断子字符串长度j-i是否大于等于K-1
代码示范
#include <iostream>
using namespace std;
int main()
{
int N;
cin >> N;
char beginc;
char endc;
string buff;
cin >> buff >> beginc >> endc;
int cnt = 0;
for (int i = 0; i < buff.length(); i++)
{
if (buff[i] == beginc)
{
for (int j = i + 1; j < buff.length(); j++)
{
if (buff[j] == endc && (j - i >= N - 1))
{
cnt++;
}
}
}
}
cout << cnt << endl;
return 0; //程序正常结束的标志
}
- 算法思维
上面的代码没有通过全部测试用例,部分超时
第一步输入和第二步输出的时间复杂度都是常数阶
主要的时间复杂度来自第二步
时间复杂度为O(N2)
- 可用算法
- 双指针
- 单条件适合,双条件比较复杂
- 计数
提高效率两种方式
- 双指针
- 空间换时间
分别存放a和b的出现的位置的下标 - 算法思维
/* G题 - 方式二:拿空间换时间 */
#include <iostream>
#include <vector>
using namespace std;
int main()
{
/*第一步:输入*/
int N;
cin >> N;
char begC, endC;
string buff;
cin >> buff >> begC >> endC;
/*第二步:拿空间换时间*/
vector begBuff, endBuff;
for(int i = 0; i < buff.length(); i++)
{
if(buff[i] == begC) begBuff.push_back(i);
// begC:开始字符的下标
if(buff[i] == endC) endBuff.push_back(i);
// endC:结束字符的下标
}
/*第三步:计数*/
int cnt = 0;
for(int i = 0; i < begBuff.size(); i++)
{
for(int j = 0; j < endBuff.size(); j++)
{
if(endBuff[j]-begBuff[i] >= N-1)
cnt++;
}
}
/*第三步:输出*/
cout << cnt << endl;
return 0; // 程序正常结束的标记
}
优化1
- 计数,i一定时,第一个j符合条件,后面的j的下标也肯定符合条件,可以直接加上,endBUff.size() - j
- 计数循环时,j没必要每次都从0开始,可以从当前位置开始
- 数据类型的边界,cnt从int换成long long
- 错误从运行错误编程答案错误,并且测试用例通过80%表示不是逻辑错误,而是用例规模问题,整数溢出
/* G题 - 方式二:拿空间换时间的优化版 */
#include <iostream>
#include <vector>
using namespace std;
int main()
{
/*第一步:输入*/
int N;
cin >> N;
char begC, endC;
string buff;
cin >> buff >> begC >> endC;
/*第二步:拿空间换时间*/
vector begBuff, endBuff;
for(int i = 0; i < buff.length(); i++)
{
if(buff[i] == begC) begBuff.push_back(i);
// begC:开始字符的下标
if(buff[i] == endC) endBuff.push_back(i);
// endC:结束字符的下标
}
/*第三步:计数*/
long long cnt = 0; //3.考虑数据规模,整数溢出
int curj = 0;
for(int i = 0; i < begBuff.size(); i++)
{
//2.每次从当前位置开始
for(int j = curj; j < endBuff.size(); j++)
{
if(endBuff[j]-begBuff[i] >= N-1)
{
cnt += endBUff.size() - j; //1.后续的条件都满足
curj = j;
break;
}
}
}
/*第三步:输出*/
cout << cnt << endl;
return 0; // 程序正常结束的标记
}
优化2
优化两次循环的次数
二分法查找
为保证数据是有序的,外循环i++是累加的,有序
数据比较多的时候,对内循环进行二分查找
用双指针算法
/* G题 - 方式二:拿空间换时间的优化版 */
#include <iostream>
#include <vector>
using namespace std;
int main()
{
/*第一步:输入*/
int N;
cin >> N;
char begC, endC;
string buff;
cin >> buff >> begC >> endC;
/*第二步:拿空间换时间*/
vector begBuff, endBuff;
for(int i = 0; i < buff.length(); i++)
{
if(buff[i] == begC) begBuff.push_back(i);
// begC:开始字符的下标
if(buff[i] == endC) endBuff.push_back(i);
// endC:结束字符的下标
}
/*第三步:计数*/
long long cnt = 0; //3.考虑数据规模,整数溢出
int curj = 0;
for(int i = 0; i < begBuff.size(); i++)
{
//二分查找,有序数列
int left = 0;
int right = endBuff.size();
while(left + 1 < right)
{
int mid = (left + right) / 2;
if (endBuff[mid] - begBuff[i] >= N - 1)
right = mid;
else
left = mid;
}
cnt += endBuff.size() - right;
}
/*第三步:输出*/
cout << cnt << endl;
return 0; // 程序正常结束的标记
}