【笔试强训day16】
目录
第一题:字符串替换
描述
输入:
返回值:
第二题:神奇数
输入描述:
输出描述:
输入
输出
第三题:DNA序列
描述
输入描述:
输出描述:
输入:
输出:
输入:
输出:
第一题:字符串替换
题目链接:字符串替换_牛客题霸_牛客网
描述
请你实现一个简单的字符串替换函数。原串中需要替换的占位符为"%s",请按照参数列表的顺序一一替换占位符。若参数列表的字符数大于占位符个数。则将剩下的参数字符添加到字符串的结尾。
给定一个字符串A,同时给定它的长度n及参数字符数组arg,请返回替换后的字符串。保证参数个数大于等于占位符个数。保证原串由大小写英文字母组成,同时长度小于等于500。
示例1
输入:
"A%sC%sE", 7, [B,D,F], 3
返回值:
"ABCDEF"
这是一道简单的模拟题。两个指针,一个指针遍历字符串A,一个指针遍历arg。
遍历字符串A,遇到%则判断它的后面一个字符是不是s,如果是的话,就替换成arg中的字符。比如字符串A中 i 位置对应的字符是%,那么在确保 i + 1不越界的情况下去检查 i + 1位置的字符是否为s,如果是的话, 就把arg中对应的字符拿过来替换,然后指针指向arg的下一个位置。最终遍历完字符串A后,如果指向arg中字符的指针还没走到尾,那么继续把arg中剩余的字符加入到答案来。
思想是这么个思想,但最终解题的时候并不会在原字符串上动刀,而是另外开一个string来存储结果。
class StringFormat {
public:
string formatString(string A, int n, vector<char> arg, int m)
{
string ans;
int j = 0;//指向arg的指针
for(int i = 0; i < n; i++)
{
if(A[i] == '%')
{
if(i + 1 < n && A[i + 1] == 's')
{
ans += arg[j++];//替换
i++;//i当前指向%,此处++后跳过了%,循环中在++,跳过字符s
}
else
{
//如果不能替换,则原样保留下来
ans += A[i];
}
}
else
{
//不能替换,则原样保留下来
ans += A[i];
}
}
//arg可能还有字符剩余
while(j < m)
{
ans += arg[j];
j++;
}
return ans;
}
};
第二题:神奇数
题目链接:神奇数_牛客笔试题_牛客网
给出一个区间[a, b],计算区间内“神奇数”的个数。
神奇数的定义:存在不同位置的两个数位,组成一个两位数(且不含前导0),且这个两位数为质数。
比如:153,可以使用数字3和数字1组成13,13是质数,满足神奇数。同样153可以找到31和53也为质数,只要找到一个质数即满足神奇数。
输入描述:
输入为两个整数a和b,代表[a, b]区间 (1 ≤ a ≤ b ≤ 10000)。
输出描述:
输出为一个整数,表示区间内满足条件的整数个数
示例1
输入
11 20
输出
6
简单举个例子:14
14本身不是质数,但是14可以组合出41,是质数,所以14是神奇数。通过这个例子,我们应该把14当做一个数字1和一个数字4来看,它们随便组合,无关顺序。
题目中提到,组成的两位数中不能含有前导零,比如02这样就是不符合题意的。
下面分析如何解决这个问题。
我们把输入的数不当做一个十进制数看,而是拆分成一位一位看。所以第一个问题自然就是如何拆分。比如数字12345,怎么拿到这个数的每一位?反复模10,除10即可。拆分数字引申出来的另一个问题是用什么存储这些单独的一位数,我采用vector来存。
接下来两层for循环枚举所有排列组合的情况,然后一一判断哪些是质数(素数)即可。
问题又来了——如何判断素数。可以用试除法来判断。比如要判断n是否为素数,就用n来依次除上2到sprt(n)之间的数(包含两头),如果都除不尽,就证明是素数;反之,则不是。
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
//判断一个数是否是素数
bool isPrime(int n)
{
for(int i = 2; i <= sqrt(n); i++)
{
if(n % i == 0)
return false;
}
return true;
}
//判断一个数是否是神奇数
int check(int n)
{
vector<int> arr;
//拆分数字放到数组中
while (n)
{
arr.push_back(n % 10);
n /= 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 1;
}
}
}
return 0;
}
int main()
{
int a, b;
cin >> a >> b;
int ans = 0;
for(int i = a; i <= b; i++)
{
//如果是神奇数则返回1,否则返回0
ans += check(i);
}
cout << ans << endl;
return 0;
}
第三题:DNA序列
题目链接:DNA序列_牛客题霸_牛客网
描述
一个 DNA 序列由 A/C/G/T 四个字母的排列组合组成。 G 和 C 的比例(定义为 GC-Ratio )是序列中 G 和 C 两个字母的总的出现次数除以总的字母数目(也就是序列长度)。在基因工程中,这个比例非常重要。因为高的 GC-Ratio 可能是基因的起始点。
给定一个很长的 DNA 序列,以及限定的子串长度 N ,请帮助研究人员在给出的 DNA 序列中从左往右找出 GC-Ratio 最高且长度为 N 的第一个子串。
DNA序列为 ACGT 的子串有: ACG , CG , CGT 等等,但是没有 AGT , CT 等等
数据范围:字符串长度满足 1≤𝑛≤1000 1≤n≤1000 ,输入的字符串只包含 A/C/G/T 字母
输入描述:
输入一个string型基因序列,和int型子串的长度
输出描述:
找出GC比例最高的子串,如果有多个则输出第一个的子串
示例1
输入:
ACGT
2
输出:
CG
说明:
ACGT长度为2的子串有AC,CG,GT3个,其中AC和GT2个的GC-Ratio都为0.5,CG为1,故输出CG
示例2
输入:
AACTGTGCACGACCTGA
5
输出:
GCACG
说明:
虽然CGACC的GC-Ratio也是最高,但它是从左往右找到的GC-Ratio最高的第2个子串,所以只能输出GCACG。
固定长度的滑动窗口问题。
滑动窗口问题一般都是以下几个步骤:
1)进窗口
2)判断
3)出窗口
4)更新
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s;
int N;//窗口长度
cin >> s >> N;
int left = 0, right = 0, begin = 0;//需要记录答案的起始下标begin
int count = 0, maxCount = 0;//count表示当前窗口内C和G的数量,maxCount表示当前C和G的最大数量
//很固定的滑动窗口写法
int n = s.size();
while(right < n)
{
if(s[right] == 'C' || s[right] == 'G')//进窗口+判断
count++;
while(right - left + 1 > N)//出窗口
{
//如果C或G被挤出窗口,那么需要维护count
if(s[left] == 'C' || s[left] == 'G')
count--;
left++;
}
if(right - left + 1 == N)//更新结果
{
if(count > maxCount)
{
maxCount = count;
begin = left;
}
}
right++;
}
//截取答案并输出
cout << s.substr(begin, N) << endl;
return 0;
}