C++日常刷题积累
今日刷题汇总 - day016
1、字符串替换
1.1、题目
1.2、思路
读完题,知道让实现将字符数组中的字符元素将对字符串中占位符的进行替换,其中如果数组有剩余就尾插到最后返回替换后的字符串即可。那么,根据示例和题目要求分析思考后,既然占位符是固定格式,我就想着看成整体,直接遍历字符串,遇见’%'先不做处理,遇见’s’再执行替换处理,占位符替换完后,再看字符数组是否为空,不为空就直接尾插到需要返回的字符串即可。那么,接下来就是程序实现。
1.3、程序实现
首先,这道题属于ACM写核心函数即可,那么这里提供了字符串A,长度n,字符数组arg,以及个数m,那么就遍历A字符串,判断遇见’%‘不管直接++,遇见‘s’再执行将arg中字符数组替换尾插到当时遍历到的字符后面即可,同时替换一个个数m减一个,遍历完最后看m是否为空,不为空则继续尾插剩余字符即可。值得说一下的是,对于不管’%‘直接遇见’s’再处理的逻辑细节,详细的说就是占位符’%s’是被看作一个整体,那么遇见‘%’先不管,但是会进入if对%位置开始进行替换,然后执行i++,表示处理整体替换。也就是,相当于直接替换的位置是1换2.
class StringFormat {
public:
string formatString(string A, int n, vector<char> arg, int m)
{
string retstr;
int j = 0;
for(int i = 0;i < n;i++)
{
if(i+1 < n && A[i] == '%' && A[i+1] == 's')
{
retstr += arg[j++];
m--;
i++;
}
else
{
retstr += A[i];
}
}
while(m--)
{
retstr += arg[j++];
}
return retstr;
}
};
2、神奇数
2.1、题目
2.2、思路
读完题,知道给出一个区间[a, b],计算区间内“神奇数”的个数。其中,神奇数表示该数只要存在任意不同位置的两个数位,组成一个两位数是质数,那么满足神奇数。注意题目中说明不能计入前导0的情况,即103,取十位和个位出现03则不能计入这样的情况。那么,基本思路就是遍历区间,判断是否满足神奇书,其中要对遍历的数字进行位数转换再组合以及判断前导0的处理和判断是否是质数(素数)。那么,接下来就是程序实现。
2.3、程序实现
首先,根据题目要求输入区间a和b,然后遍历这个区间累计满足神奇数的个数,这里封装一个check函数,用于判断是否满足神奇数。
#include <iostream>
using namespace std;
bool check(int x)
{
}
int main()
{
int a,b;
cin >> a >> b;
int count = 0;
for(int i = a;i <= b;i++)
{
if(check(i))
count++;
}
cout << count << endl;
return 0;
}
接着,封装check函数,根据思路分析需要处理位数,所以可以使用数组arr存储每一位的数值,然后枚举组合两位数情况,其中注意排除自身和自身组合,以及排除前导为0的情况,再判断组合的数是否满足质数。其中这里利用将arr[i]作为十位数的数,所以乘以10再将arr[j]加上,拿去判断isprime是否满足质数即可
#include <iostream>
#include <vector>
using namespace std;
bool isprime(int num)
{
}
bool check(int x)
{
vector<int> arr;
while(x)
{
arr.push_back(x % 10);
x /= 10;
}
for(int i = 0;i < arr.size();i++)
{
for(int j = 0;j < arr.size();j++)
{
if(i != j && arr[i] != 0)
{
if(isprime(arr[i]*10 + arr[j]))
{
return true;
}
}
}
}
return false;
}
int main()
{
int a,b;
cin >> a >> b;
int count = 0;
for(int i = a;i <= b;i++)
{
if(check(i))
count++;
}
cout << count << endl;
return 0;
}
接着,完善封装isprime函数,利用质数的性质,除了1和本身外没有能将它整除的数,所以如果能被2到num-1的数整除说明不满足质数,否则满足。
#include <iostream>
#include <vector>
using namespace std;
bool isprime(int num)
{
if(num < 2)
return false;
for(int i = 2;i < num;i++)
{
if(num % i == 0)
return false;
}
return true;
}
bool check(int x)
{
vector<int> arr;
while(x)
{
arr.push_back(x % 10);
x /= 10;
}
for(int i = 0;i < arr.size();i++)
{
for(int j = 0;j < arr.size();j++)
{
if(i != j && arr[i] != 0)
{
if(isprime(arr[i]*10 + arr[j]))
{
return true;
}
}
}
}
return false;
}
int main()
{
int a,b;
cin >> a >> b;
int count = 0;
for(int i = a;i <= b;i++)
{
if(check(i))
count++;
}
cout << count << endl;
return 0;
}
2.4、程序实现 – 优化
基于上面的蛮力法,可进行一些细节优化,第一个当输入的区间a小于十时,也就是无法组成两位数,可以直接略过,第二个优化,素数的判定,具有完全平方数的性质,也就是说能被3整除就能被9整除,所以直接对num进行开根号处理,缩小遍历区间。优化后完成代码如下:
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
bool isprime(int num)
{
if(num < 2)
return false;
for(int i = 2;i <= sqrt(num);i++)
{
if(num % i == 0)
return false;
}
return true;
}
bool check(int x)
{
vector<int> arr;
while(x)
{
arr.push_back(x % 10);
x /= 10;
}
for(int i = 0;i < arr.size();i++)
{
for(int j = 0;j < arr.size();j++)
{
if(i != j && arr[i] != 0)
{
if(isprime(arr[i]*10 + arr[j]))
{
return true;
}
}
}
}
return false;
}
int main()
{
int a,b;
cin >> a >> b;
int count = 0;
for(int i = max(a,10);i <= b;i++)
{
if(check(i))
count++;
}
cout << count << endl;
return 0;
}
3、DNA序列
3.1、题目
3.2、思路
读完题,知道需要处理一个由A/C/G/T组成的DNA序列,然后求其GC比例最高且长度为N的第一个子串并输出。这里比例最高就是出现的次数最多,也就是输出子串C和G次数最多的连续的子串即可。那么,可以蛮力法利用双指针left和right遍历,统计C和G的个数count,每一次遍历的长度到输入的长度n时,就比较依次count的最大值,然后将lef指针向后移动一位,right指针也返回left,count也清0重新计数,以此循环遍历所有结果得到的最大值count的子串就是需要输出的子串。
不难发现,这样会导致很多重复的无用功,也发现两个指针的移动方向一致,且保持着固定的长度n,进行更新指针位置,那么就想到了滑动窗口的思路。所以这里相较之下使用滑动窗口更优化。
那么滑动窗口的基本步骤:
(1)、进窗口:这里遇见C或G就进窗口;
(2)、判断窗口:当窗口大于输入的长度n时就移动窗口;
(3)、出窗口:移动窗口时,如果左指针是C或G就出窗口;
(4)、更新结果:更新记录的数量count表示每一组窗口中最大或最高比例的C/G;
那么接下来就是程序实现。
3.3、程序实现 – 蛮力法
首先,按照题目需要输入DNA字符串和输入长度n,然后定义count对C和G计数,定义maxcount对最后的最大比例计数标记rleft需要返回的子串位置。然后执行遍历,如果输入的字符串不足需要的子串长度直接break,否则利用遍历统计每组长度n的子串中的C和G的个数,每一组遍历完再与maxcount比较,以保存最大比例,和更新子串下标即可。
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str;
cin >> str;
int n;
cin >> n;
int rleft = 0;
int count = 0;
int maxcount = -1;
for (int i = 0; i < str.size(); i++)
{
count = 0;
if (str.size() - i < n)
break;
for (int j = i; j < n+i; j++)
{
if (str[j] == 'C' || str[j] == 'G')
{
count++;
}
}
if (maxcount < count)
{
maxcount = count;
rleft = i;
}
}
cout << str.substr(rleft, n);
return 0;
}
3.4、程序实现 – 滑动窗口
利用滑动窗口思路,优化指针回溯问题,同样写好输入和定义好需要的变量,然后遍历字符串如果是C或G就进窗口计数,窗口保持长度为n的大小,所以判断当窗口超过n时就滑动窗口left++,并且如果str[left]是字符C或G则出窗口count–,同时刚好等于窗口大小时更新maxcount和子串下标即可。
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str;
cin >> str;
int n;
cin >> n;
int left = 0;
int rleft = 0;
int right = 0;
int count = 0;
int maxcount = -1;
while(right < str.size())
{
//进窗口
if (str[right] == 'C' || str[right] == 'G')
count++;
//判断出窗口,滑动窗口
if (right - left + 1 > n)
{
if (str[left] == 'C' || str[left] == 'G')
count--;
left++;
}
//判断窗口边界
if (right - left + 1 == n)
{
if (maxcount < count)
{
maxcount = count;
rleft = left;
}
}
right++;
}
cout << str.substr(rleft, n);
return 0;
}