1. 替换空格
实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
思路:
首先要确定是不是在本身上修改,如果是,则要题目保证内存足够。否则,可以考虑自己new空间。
代码:
class Solution {
public:
void replaceSpace(char *str, int length) {
if (str == nullptr || length < 0) return ; //首要判断字符串是否为空串,满足一个条件即为空串
int counter = 0;
for (int i=0; i < length; i++)
if(str[i]==' ') counter++; //先判断字符串中有多少个空格字符,从而防止进行下一步操作时出现越界现象
// 假设是在原有的基础上进行替换,且保证内存足够
int newlength = length + counter*2;//替换空格后的字符串长度
int p = length;
while(newlength >= 0)
{
if(str[p] != ' ')
str[newlength--] = str[p--];
else
{
str[newlength--] = '0';
str[newlength--] = '2';
str[newlength--] = '%';
p--;
}
}
}
};
2.字符串的排列
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
思路:本题求整个字符串的全排列可以看做两步
1)首先求出所有可能出现在第一位置的字母,即begin与后面所有与它不同的字母进行交换
2)固定第一个字母,求后面字母的全排列,即递归此时begin = begin+1
代码:
class Solution {
public:
vector<string> (string str) {
vector<string> res;//函数返回的是一个字符串类型的vector。
if(str.size() <= 0)
return res;
Perm(res, str, 0); //从字符串数组的第0位开始
sort(res.begin(), res.end()); //vector使用sort函数进行排序(按字典序),注意res.end指向的是最后一个元素指向的下一个位置
return res;
}
void Perm(vector<string> &res, string str, int begin)//善于在参数中使用引用符号
{
//第一次遍历结束条件
if(begin == str.size() - 1)
res.push_back(str);
for(int i=begin; i<str.size(); i++)
{
//有与begin位重复的字符串不进行交换,跳过
if(i != begin && str[i] == str[begin])
continue;
swap(str, i, begin);
Perm(res, str, begin+1);
swap(str, i, begin);//为了防止重复的情况,还需要将begin处的元素重新换回来
}
}
void swap(string &str, int i, int j)
{
char c;
c = str[i];
str[i] = str[j];
str[j] = c;
}
};
扩展:
如果不是求字符的所有排列,而是求字符的所有组合,应该怎么办呢? 还是输入三个字符 a、b、c,则它们的组合有 a、b、c、ab、ac、bc、abc。 当交换字符串中的两个字符时,虽然能得到两个不同的排列,但却是同一个组合。比如 ab 和 ba 是 不同的排列,但只算一个组合。
思路: 如果输入 n 个字符,则这 n 个字符能构成长度为 1 的组合、长度为 2 的组合、….、长度为 n 的组合。在求 n 个字符的长度为 m ( 1<=m<=n) 的 组合的时候,我们把这 n 个字符分成两部分:第一个字符和其余的所有字符。如果组合里包含第一个字符,则下一步在剩余的字符里选取 m-1个字 符:如果组合里不包含第一个字符,则下一步在剩余的 n-1 个字符里选取 m 个字符。也就是说,我们可以把求 n 个字符组成长度为 m 的组合的问题分解成两个子问题,分别求 n-1 个字符串中长度为 m-1 的组合,以及求 n-1 个字符的长度为 m 的组合。这两个子问题都可以用递归的方式解决。
实现方法:位图法。
譬如选择表示为“1”,不选择表示为“0”,那么abc的组合方法有:001(a),010(b),011(ab),100(c),101(ac),110(cb),111(abc)。000是没选择,不行。共有2strlen(“abc”)−1中组合。
代码:
#include<stdio.h>
#include<string.h>
void Combination(char *str)
{
if(str == NULL)
return ;
int len = strlen(str);
int n = 1<<len;//1左移len位,左移一位相当于乘以2,右移一位相当于除以2,求得的n表示共有几种组合
int i=1;
int j=0;
for(i=1;i<n;i++) //求长度为i的组合个数
{
for(j=0;j<len;j++)
{
int temp = i;
if(temp & (1<<j)) //对应位上为1,则输出对应的字符,与运算符和求相应数减1的模相等价,如2&1等价于2%2.
{
printf("%c",*(str+j));
}
}
printf("\n");
}
}
3. 字符串中第一个只出现一次的字符
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).
思路:最直观的想法是从头开始扫描这个字符串中的每个字符。当访问到某字符时拿这个字符和后面的每个字符相比较,如果在后面没有发现重复的字符,则该字符就是只出现一次的字符。如果字符串有n个字符,每个字符可能与后面的O(n)个字符相比较,因此这种思路的时间复杂度是O(n2)
**以空间换时间:**定义一个哈希表(外部空间),其键值(Key)是字符,而值(Value)是该字符出现的次数。
我们需要从头开始扫描字符串两次:
(1)第一次扫描字符串时,每扫描到一个字符就在哈希表的对应项中把次数加1。(时间效率O(n))
(2)第二次扫描时,每扫描到一个字符就能从哈希表中得到该字符出现的次数。这样第一个只出现一次的字符就是符合要求的输出。(时间效率O(n))
(由于256个ASCII字符,空间长度为常数,所以空间复杂度为O(1)).
代码:
class Solution {
public:
int FirstNotRepeatingChar(string str) {
if(str.length() == 0) return -1;
int hash[256] = {0};
for(int i = 0; i < str.length(); i++)
{
hash[str[i]] ++;
}
for(int i = 0; i < str.length(); i++)
if(hash[str[i]] == 1)
return i;
return -1;
}
};
4. 在字符流中第一个只出现一次的字符
请实现一个函数,用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符go时,第一个只出现一次的字符是g;当从该字符流中读出前6个字符google时,第一个只出现一次的字符是l。
思路:
字符只能一个一个从字符流中读出来,因此要定义一个容器来保存字符以及其在字符流中的位置。
由于要在O(1)时间内往数据容器中插入字符,及其对应的位置,因此这个数据容器可以用哈希表来实现,以字符的ASCII码作为哈希表的键值key,字符对应的位置作为哈希表的值value。
开始时,哈希表的值都初始化为-1,当读取到某个字符时,将位置存入value中,如果之前读取过该字符(即value>=0),将value赋值为-2,代表重复出现过。最后对哈希表遍历,在value>=0的键值对中找到最小的value,该value即为第一个只出现一次的字符,ASCII码为key的字符即为所求字符。
代码:
class Solution
{
private:
int occ[256];
int index;
public:
//无参数的构造函数
Solution(){
index = 0;
for(int i = 0; i < 256; i++)
occ[i] = -1; // -1没出现过 -2出现过 >0出现顺序
}
//Insert one char from stringstream
void Insert(char ch)
{
if(occ[ch] == -1) occ[ch] = index;//存放的是该字符在字符流中对应字符的位置
else if(occ[ch] >= 0) occ[ch] = -2;//表示该字符重复出现
index++;
}
//return the first appearence once char in current stringstream
char FirstAppearingOnce()
{
int minIndex = -1;
char ch;
for(int i = 0; i < 256; i++)
{
if(occ[i] >= 0)
{
if(minIndex == -1 || minIndex > occ[i])
minIndex = occ[i], ch = i;
}
}
if(minIndex == -1) return '#';
return ch;
}
};
5 . 左旋转字符
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
思路:逆置三次即可。逆置可采用交换方法。
代码:
class Solution {
public:
string LeftRotateString(string str, int n) {
if(str.length() <= 0) return "";
reverse(str, 0, n-1);
reverse(str, n, str.length()-1);
reverse(str, 0, str.length()-1);
return str;
}
//采用swap方法进行逆置
void reverse(string &str, int begin, int end)
{
char tmp;
while(begin < end)
{
tmp = str[begin];
str[begin] = str[end];
str[end] = tmp;
begin ++;
end --;
}
}
};
附加题:反转数字
将给出的整数x翻转。
例1:x=123,返回321
例2:x=-123,返回-321
你有思考过下面的这些问题么?
如果整数的最后一位是0,那么输出应该是什么?比如10,100
你注意到翻转后的整数可能溢出吗?假设输入是32位整数,则将翻转10000000003就会溢出,你该怎么处理这样的样例?抛出异常?这样做很好,但是如果不允许抛出异常呢?这样的话你必须重新设计函数(比如添加一个额外的参数)。
示例1
输入
-123
输出
-321
代码:
class Solution {
public:
/**
*
* @param x int整型
* @return int整型
*/
int reverse(int x) {
// write code here
long long res = 0;
while(x)
{
res = res* 10 + (x % 10);
x = x/10;
}
//处理异常
return (res < INT_MIN || res > INT_MAX) ? 0 : res;
}
};
6. 翻转单词顺序
牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
思路:和上题一样,先整体翻转,然后在对每一个单词进行翻转(以空格为标识)
代码:
class Solution {
public:
string ReverseSentence(string str) {
reverse(str, 0, str.length()-1);
int begin = 0;
//去除前置空格,注意for语句中的判断语句,两个条件不能拆开
for(int i = 0; i < str.length() && str[i] == ' '; i++)
begin ++;
for(int i = 0; i < str.length(); i ++)
{
if(str[i] == ' ')
{
reverse(str, begin, i-1);
begin = i + 1;
// 去除多个空格情况
while(begin < str.length() && str[begin] == ' ')
begin ++;
}
}
int end = str.length() - 1;
// 去除末尾多余空格
while(end < str.length() && str[end] == ' ')
end --;
if(begin < end)
reverse(str, begin, end);
return str;
}
void reverse(string &str, int begin, int end)
{
char tmp;
while(begin < end)
{
tmp = str[begin];
str[begin] = str[end];
str[end] = tmp;
begin ++;
end --;
}
}
};
7. 把字符串转换成数字
将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0
思路:如果字符串能转化成符合题意的整数,那么字符串中的字符肯定实在’0’ ~'9’之间的,符号 + 或者 - 如果出现的话,那么肯定是出现的字符串的第一个元素的位置,如果出现在字符串的中间的某个位置,这个字符串就不可以转化为数字。
再有就是:res = (res << 1) + (res << 3) + (str[i] & 0xf);
对这句话的解释: (res << 1) + (res << 3),左移一位是乘以2,左移3位是乘以8,加起来相当于res*10;
(str[i] & 0xf) 相当于 str[i] - ‘0’,为什么呢?因为 字符’0’ ~ ‘9’ 的SACII码值的低四个二进制位就是 0 ~ 9, 0xf 是数字15,二进制位1111,这样操作之后就可以将 ‘0’ 转化为0,…,将‘9’转化为‘9’。
整体看这句话:res = res * 10 + str[i] - ‘0’。例如字符串为‘123’;
那么单步运行:res = 1; res = 1 * 10 + 2 = 12; res = 12 * 10 + 3 = 12。这句话就是这个意思。
代码:
class Solution {
public:
int StrToInt(string str) {
int len = str.size();
if(len <= 0)
return 0;
int symbol = 1;
long long res = 0;
if(str[0] == '-')
symbol = -1;
for(int i = (str[0] == '+' || str[0] == '-')? 1 : 0; i < len; ++ i)
{
if(str[i] >= '0' && str[i] <= '9')
res = (res << 1) + (res << 3) + (str[i] & 0xf);
else
return 0;
}
return res * symbol;
}
};
8 . 扑克牌顺子
LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张_)…他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子…LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。
思路:
先不去考虑能否构成顺子,有一些先决条件必须满足,否则判断顺子无从谈起:
(1)一共只有5张牌 <=>数组大小为5
(2)为了方便起见,牌的面值从0到13,在这之外的,就是不对的 <=>元素值取值[0,13]
(3)大小王充当癞子使用,不允许出现对子 <=>满足前两条后,除了0以外,不允许出现重复的,而且0不得出现5次
代码:
class Solution {
public:
bool IsContinuous( vector<int> numbers ) {
//数组大小为5
if(numbers.size() != 5)
return false;
sort(numbers.begin(), numbers.end());
int numOfZero = 0, numOfInterval = 0;
for(int i = 0; i < numbers.size() - 1; i ++)
{
//牌的面值范围在[0,13]
if(numbers[i] < 0 || numbers[i] > 13)
return false;
//统计大王小王(即牌为0)的个数
if(numbers[i] == 0)
{
numOfZero ++;
continue;
}
//出现重复非0牌值,则不能构成顺子
if(numbers[i] == numbers[i+1])
return false;
//最大值和最小值之间差多少(由于排好序的,注意for循环中的i条件是小于该数组长度-1,即循环体只需要执行numbers.size()-1次[0,number.size()-1))
numOfInterval += numbers[i+1] - numbers[i] - 1;
}
return numbers[4] !=0 && (numOfZero >= numOfInterval ? true : false);
}
};
9. 正则表达式
请实现一个函数用来匹配包括’.‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配
思路:
考虑号的情况。即 当(pattern+1) == ''的时候,由于可以匹配0次,故要么匹配,要么不匹配。如果不匹配的话,直接返回str,pattern+2,如果匹配,则一直匹配(str+1,pattern)或者只匹配这一次(str+1,pattern+2)即可
其他情况,正常来就行,当str==*pattern就放行,或者为点的时候,但是点不能匹配空,所以要求点必须要匹配一个字符。
代码:
class Solution {
public:
bool match(char* str, char* pattern)
{
if(str == nullptr || pattern == nullptr)
return false;
if(*pattern == '\0')
return *str == '\0' ? true : false;
if(*(pattern + 1) == '*')
{
if(*str == *pattern || *pattern == '.' && *str != '\0')
//(1) 当前匹配为0;(2)当前匹配上了继续匹配;(3)当前匹配上了不继续匹配
return match(str, pattern+2) || match(str+1, pattern) || match(str+1, pattern+2);
else
//不满足if条件,肯定是不匹配的结果,则*前的字符出现的次数为0
return match(str, pattern+2);
}
//*(pattern+1) != '*'的情况下,正常匹配即可,当*str==*pattern就放行,或者为点的时候,但是点不能匹配空,所以要求点必须要匹配一个字符。
if(*str == *pattern || *pattern == '.' && *str != '\0')
return match(str+1, pattern+1);
return false;
}
};
10. 表示数值的字符串
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。
思路:
首先可以推出格式(±)(num1)(.num2)(Ee(num3)),其中num123均为1-9的数字串。当Ee存在时,后面的数字才能存在。
1、使用正则表达式处理
2、一步一步按格式处理
代码1:
//使用c++中的正则表达式不要忘记其头文件(regex = regular experession)
#include<regex>
class Solution {
public:
bool isNumeric(char* string)
{
regex pattern("[\\+\\-]?\\d*(\\.\\d+)?([eE][\\+\\-]?\\d+)?");
return regex_match(string, pattern);
}
};
[\+\-]? 正或负符号出现与否
* \d* 整数部分是否出现,如-.34 或 +3.34均符合
* (\.\d+)? 如果出现小数点,那么小数点后面必须有数字(*和+的区别,*可表示零次,+表示必须出现一次);否则一起不出现
* ([eE][\+\-]?\d+)? 如果存在指数部分,那么e或E肯定出现,+或-可以不出现,紧接着必须跟着整数;或者整个部分都不出现
代码2:
class Solution {
public:
bool isNumeric(char* str)
{
if(str == nullptr) return false;
// (+-)(num1)(.num2)[Ee](+-)(num3)
// 1 最开始的符号位
if(*str == '+' || *str == '-') str++;
// 2 num1,-.34 或 +3.34均符合
while(*str != '\0')
{
if(*str < '0' || *str > '9') break;
str++;
}
// 3 .num2
if(*str == '.')
{
str++;
// 类似12.是符合要求的
while(*str != '\0')
{
if(*str < '0' || *str > '9') break;
str++;
}
}
// 4 Ee
if(*str == 'E' || *str == 'e')
{
str++;
// 类似12e不符合要求
if(*str == '\0') return false;
if(*str == '+' || *str == '-') str++;
while(*str != '\0')
{
if(*str < '0' || *str > '9') break;
str++;
}
}
return *str == '\0';
}
};
11. 字节跳动—编程2
有一个仅包含’a’和’b’两种字符的字符串s,长度为n,每次操作可以把一个字符做一次转换(把一个’a’设置为’b’,或者把一个’b’置成’a’);但是操作的次数有上限m,问在有限的操作数范围内,能够得到最大连续的相同字符的子串的长度是多少。
输入描述:
第一行两个整数 n , m (1<=m<=n<=50000),第二行为长度为n且只包含’a’和’b’
的字符串s。
输出描述:
输出在操作次数不超过 m 的情况下,能够得到的 最大连续 全’a’子串或全’b’子串的长度。
输入例子1:
8 1
aabaabaa
输出例子1:
5
例子说明1:
把第一个 'b' 或者第二个 'b' 置成 'a',可得到长度为 5 的全 'a' 子串。
思路: 该字符串非 a 即 b 也就是说在区间 l~r之间把所有字符变为 a 所需的步骤数是 该区间内 字符b 的数量。反之亦然.
用数组 count[i] 表示 字符串中位置区间 0~i 包含的 a 的个数,则 区间 l~r 的 a 的个数为 count[r] - count[l - 1]
b 的个数用 a 的个数算出 即 区间 l~r 的 b 的个数为 r + 1 - count[r] - (l + 1 - 1 - count[l - 1]) = r + 1 - l - count[r] + count[l - 1]
在区间 l~r 的 a 和 b 的个数已知的情况下
若 区间长度step内的 a 的个数 <= m 则 可以通过 m 个步骤 产生 长度为step的连续字符串 b
若 区间长度step内的 b 的个数 <= m 则 可以通过 m 个步骤 产生 长度为step的连续字符串 a
归纳为 :若 区间长度step内的字符 b 或字符 a 的个数 <= m 则 可以通过 m 个步骤 产生 长度为step的字符串(不管是全a还是全b)
这样 就可以直接计算出一个字符串长度(区间长度step)是否可行,因此不需要进行递推,可以直接进行二分搜索,得到最大长度。
检查一个长度step是否可行的时间复杂度为O(n),二分搜索的时间复杂度为O(log n)。
因此,该方法总的时间复杂度为 O(n*log n)
代码:
#include<iostream>
#include<string>
#include<cstring>
using namespace std;
//数组区间
int count[50005];
int n,m;
//检查当前区间长度(step)是否能在 m 个步骤内实现全 a 或 全 b
bool func(int step)
{
for(int i = 0;i + step < n;i++)
{
if(m >= step + 1 - (count[i + step] - count[i - 1]))
return true; //检查 a--》 公式 m>=区间内 b 的个数
if(m >= count[i + step] - count[i - 1])
return true; //检查 b--》 公式: m>=区间内 a 的个数
}
return false;
}
int main()
{
cin>>n>>m;
string str;
cin>>str;
//输入并计算出 count 数组
int sum = 0;
//统计0~i区间内的a的数量分别保存在每个count[i]位置
for(int i = 0;i < str.size();i++)
{
if(str[i] == 'a')
count[i] = ++sum;
else
count[i] = sum;
}
//二分搜索最大区间值
int l = 0,r = n-1,mid;
while(l < r)
{
//取中点,确定区间的大小
mid = l + (r - l) / 2;
//如果当前区间长度满足情况,需要睁增大区间长度
if(func(mid))
{
l = mid + 1;
}
//反之缩小区间长度
else
{
r = mid;
}
}
cout<<l<<endl;
return 0;
}
12. 万万没想到之聪明的编辑
我叫王大锤,是一家出版社的编辑。我负责校对投稿来的英文稿件,这份工作非常烦人,因为每天都要去修正无数的拼写错误。但是,优秀的人总能在平凡的工作中发现真理。我发现一个发现拼写错误的捷径:
- 三个同样的字母连在一起,一定是拼写错误,去掉一个的就好啦:比如 helllo -> hello
- 两对一样的字母(AABB型)连在一起,一定是拼写错误,去掉第二对的一个字母就好啦:比如 helloo -> hello
- 上面的规则优先“从左到右”匹配,即如果是AABBCC,虽然AABB和BBCC都是错误拼写,应该优先考虑修复AABB,结果为AABCC
我特喵是个天才!我在蓝翔学过挖掘机和程序设计,按照这个原理写了一个自动校对器,工作效率从此起飞。用不了多久,我就会出任CEO,当上董事长,迎娶白富美,走上人生巅峰,想想都有点小激动呢!
……
万万没想到,我被开除了,临走时老板对我说: “做人做事要兢兢业业、勤勤恳恳、本本分分,人要是行,干一行行一行。一行行行行行;要是不行,干一行不行一行,一行不行行行不行。” 我现在整个人红红火火恍恍惚惚的……
请听题:请实现大锤的自动校对程序
输入描述:
第一行包括一个数字N,表示本次用例包括多少个待校验的字符串。
后面跟随N行,每行为一个待校验的字符串。
输出描述:
N行,每行包括一个被修复后的字符串。
代码:
#include<iostream>
#include<regex>
using namespace std;
string replace(string input, string regStr, string rep){
regex reg(regStr);
return regex_replace(input, reg, rep);
}
int main(){
int n;
cin>>n;
while(n--){
string input;
cin >> input;
cout << replace(replace(input, "(.)\\1+", "$1$1"), "(.)\\1(.)\\2", "$1$1$2") << endl;
}
return 0;
}
注意:
(.)\1+ 表示 表示任意一个字符重复两次或两次以上(括号里的点表示任意字符,后面的\1表示取第一个括号匹配的内容,后面的加号表示匹配1次或1次以上。二者加在一起就是某个字符重复两次或两次以上)
$1是第一个小括号里的内容,$2是第二个小括号里面的内容,
13. 字符串中最后一个单词长度
计算字符串最后一个单词的长度,单词以空格隔开。
输入描述:
一行字符串,非空,长度小于5000。
输出描述:
整数N,最后一个单词的长度。
代码:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string str;
getline(cin,str);//cin是遇到空格或者换行就结束了;而getline是遇到换行才结束;其中 cin 是正在读取的输入流,而 str是接收输入字符串的 string 变量的名称
int count = 0;
for(int i = str.length()-1; i >= 0; i --)
{
if(str[i] != ' ')
count ++;
else
break;
}
cout << count<< endl;
return 0;
}
14. 字符串分隔
•连续输入字符串,请按长度为8拆分每个字符串后输出到新的字符串数组;
•长度不是8整数倍的字符串请在后面补数字0,空字符串不处理。
输入描述:
连续输入字符串(输入2次,每个字符串长度小于100)
输出描述:
输出到长度为8的新字符串数组
示例1
输入
abc
123456789
输出
abc00000
12345678
90000000
代码:
#include<iostream>
#include<string>
using namespace std;
void Divide(string &str)
{
int n = str.length();
string s0;
if(n <= 8)
{
for(int i = 0; i <(8-n); i ++)
str += '0';
cout << str << endl;
return ;
}
if(n > 8)
{
string s0(str, 0, 8);//将str内,开始于0且长度顶多为8的部分作为s0的初值
cout << s0 << endl;
}
string s1(str, 8);//将str内,开始于位置8的部分当作s1的初值
Divide(s1);
}
int main()
{
string s0, s1;
getline(cin,s0);
getline(cin,s1);
Divide(s0);
Divide(s1);
return 0;
}
15. 进制转换
写出一个程序,接受一个十六进制的数,输出该数值的十进制表示。(多组同时输入 )
输入描述:
输入一个十六进制的数值字符串。
输出描述:
输出该数值的十进制字符串。
示例1
输入
0xA
输出
10
思路:
输入为字符串,且格式为0x为前缀,可以以此为切入点。
1、将输入作为字符串读取
2、倒序遍历字符串(从尾到头)
3、16进制转换10进制运算,利用乘方加以解决高位的换算。
4、以0xAA为例,从尾到头遍历到0xAA的 ‘x’ 处停止,得到最终结果输出。
代码:
#include<iostream>
#include<string>
#include<cmath>
using namespace std;
int main()
{
string str;
while(cin >> str)//保证同时输入
{
int n = str.length();
int res = 0;
for(int i = n-1; i >= 2 ; i --)
{
if(str[i] >= '0' && str[i] <= '9' || str[i] >= 'A' && str[i] <= 'F')
{
switch(str[i])
{
case 'A':
{res += 10 * pow(16, n-1-i); break;}
case 'B':
{res += 11 * pow(16, n-1-i); break;}
case 'C':
{res += 12 * pow(16, n-1-i); break;}
case 'D':
{res += 13 * pow(16, n-1-i); break;}
case 'E':
{res += 14 * pow(16, n-1-i); break;}
case 'F':
{res += 15 * pow(16, n-1-i); break;}
default:
{res += (str[i]-'0') * pow(16, n-1-i); break;}
}
}
}
cout << res << endl;
}
return 0;
}
16. 坐标移动
开发一个坐标计算工具, A表示向左移动,D表示向右移动,W表示向上移动,S表示向下移动。从(0,0)点开始移动,从输入字符串里面读取一些坐标,并将最终输入结果输出到输出文件里面。
输入:
合法坐标为A(或者D或者W或者S) + 数字(两位以内)
坐标之间以;分隔。
非法坐标点需要进行丢弃。如AA10; A1A; % ; YAD; 等。
下面是一个简单的例子 如:
A10;S20;W10;D30;X;A1A;B10A11;;A10;
处理过程:
起点(0,0)
-
A10 = (-10,0)
-
S20 = (-10,-20)
-
W10 = (-10,-10)
-
D30 = (20,-10)
-
x = 无效
-
A1A = 无效
-
B10A11 = 无效
-
一个空 不影响
-
A10 = (10,-10)
结果 (10, -10)
注意请处理多组输入输出
输入描述:
一行字符串
输出描述:
最终坐标,以,分隔
示例1
输入
A10;S20;W10;D30;X;A1A;B10A11;;A10;
输出
10,-10
思路:
(1)分隔字符串;
(2)判断子串是否符合(长度在2-3,从第二个字符开始必须是数字字符);
(3)坐标移动。
代码:
#include<iostream>
#include<string>
#include<cstddef> //std::size_t
using namespace std;
int main(){
string str;
while(cin>>str){
pair<int,int> point(0,0); //point.first point.second
size_t found = str.find_first_of(';'); //找到第一个';'的位置
int start = 0;
while(found!=string::npos){
string s1 = str.substr(start,found-start);//从start开始长度为found-start的子串
//cout << s1 << endl;
start = found+1;
found = str.find_first_of(';',found+1);
if(s1.size()>1 && s1.size()<=3){ //合法的字符个数:2或3
char c = s1[0];
int n = 0;
int invalid = 0; //数字为是否非法
for(int i=1; i<s1.size(); ++i){ //数字位判断与提取,A1A
if(s1[i]>='0'&&s1[i]<='9')
n = n*10 + (s1[i]-'0');
else{
invalid=1;
break;
}
}
if(invalid==0){
switch(c)
{
case 'A': {point.first-=n;break;}
case 'D': {point.first+=n;break;}
case 'W': {point.second+=n;break;}
case 'S': {point.second-=n;break;}
}
}
}
}
cout << point.first << ',' << point.second <<endl;
}
return 0;
}
注意:
(1)size_t find_first_of (const string& str, size_t pos = 0)
首先find_first_of该函数的返回类型是size_t类型,其次该函数表示的是从pos(默认是是0,即从头开始查找)开始查找,找到第一个和str1相匹配的子串,返回该子串的起始索引位置。
(2) pair 是 一种模版类型。每个pair 可以存储两个值。这两种值无限制。也可以将自己写的struct的对象放进去
(3)头文件cstddef与其C对应版本兼容,它是C头文件<stddef.h>较新版本,定义了常用的常量、宏、类型和函数
内的各项定义
NULL : 指针值用来表示未定义或无值
nullptr_t : nullptr的类型
size_t :一种无正负号类型,用来表示大小(比如元素的个数)
ptrdiff_t :一种带正负号类型,用来表示指针之间的距离
max_align_t:所有环境之最大齐位所对应的类型
offsetof(type,mem):表示成员mem在某个struct或union中的偏移量
17. 识别有效的IP地址和掩码并进行分类
请解析IP地址和对应的掩码,进行分类识别。要求按照A/B/C/D/E类地址归类,不合法的地址和掩码单独归类。
所有的IP地址划分为 A,B,C,D,E五类
A类地址1.0.0.0~126.255.255.255;
B类地址128.0.0.0~191.255.255.255;
C类地址192.0.0.0~223.255.255.255;
D类地址224.0.0.0~239.255.255.255;
E类地址240.0.0.0~255.255.255.255
私网IP范围是:
10.0.0.0~10.255.255.255
172.16.0.0~172.31.255.255
192.168.0.0~192.168.255.255
子网掩码为二进制下前面是连续的1,然后全是0。(例如:255.255.255.32就是一个非法的掩码)
注意二进制下全是1或者全是0均为非法
注意:
- 类似于【0...】和【127...】的IP地址不属于上述输入的任意一类,也不属于不合法ip地址,计数时可以忽略
- 私有IP地址和A,B,C,D,E类地址是不冲突的
输入描述:
多行字符串。每行一个IP地址和掩码,用~隔开。
输出描述:
统计A、B、C、D、E、错误IP地址或错误掩码、私有IP的个数,之间以空格隔开。
示例1
输入
10.70.44.68~255.254.255.0
1.0.0.1~255.0.0.0
192.168.0.2~255.255.255.0
19..0.~255.255.255.0
输出
1 0 1 0 0 2 1
思路:这道题只要理清了几类IP地址及其子网掩码的特点,就不难
1、A类地址(适用于大型网络)
IP范围:1.0.0.0~126.255.255.255
子网掩码:255.0.0.0
2、B类地址(适用于中型网络)
IP范围:128.0.0.0~191.255.255.255
子网掩码:255.255.0.0
3、C类地址(适用于小型网络)
IP范围:192.0.0.0~223.255.255.255
子网掩码:255.255.255.0
4、D类地址(组播地址,没有子网掩码)
IP范围:224.0.0.0~239.255.255.255
5、E类地址(保留地址,没有子网掩码)
IP范围:240.0.0.0~255.255.255.255
6、私网IP范围(与A、B、C类地址不冲突,即两边都可以统计)
10.0.0.0~10.255.255.255
172.16.0.0~172.31.255.255
192.168.0.0~192.168.255.255
7、错误IP地址或错误掩码
可以看出,A/B/C/D/E类IP地址包含了所有合法类型的IP地址,且有对应的子网掩码,其他的都是非法的IP地址或掩码
#include<iostream>
#include<string>
#include<vector>
using namespace std;
void splitString(const string& str, const string separator, vector<string>& res)
{
int pos1, pos2;
pos1 = 0;
pos2 = str.find(separator, pos1); //找到返回位置,找不到返回-1
string temp;
while (pos1 != pos2)
{
temp = str.substr(pos1, pos2 - pos1); //第二次循环这里,pos1等于“~”后面一个位置,而pos1=-1,pos2-pos1<0
res.push_back(temp); //所以默认截取pos1,到字符串末尾
pos1 = pos2 + 1;
if (pos1 == 0)break;
pos2 = str.find(separator, pos1);
}
}
bool checkIpNums(const vector<int>& ips)
{
if (ips.size() == 4)
{
if (ips[0] >=0 && ips[0] <= 255 && ips[1] >=0 && ips[1] <= 255
&& ips[2] >=0 && ips[2] <= 255 && ips[3] >= 0 && ips[3] <= 255)
return true;
else
return false;
}
else
return false;
}
bool validMask(const vector<int>& mask)
{
if (checkIpNums(mask))
{
int pos=3;
for (int i = 3; i >= 0; i--)
{
if (mask[i] != 0)
{
pos = i;
break;
}
}
if (pos == 3&&mask[pos]==255)return false;
if (mask[pos] == 255 || mask[pos] == 254 || mask[pos] == 252 || mask[pos] == 248 ||
mask[pos] == 240 || mask[pos] == 224 || mask[pos] == 192 || mask[pos] == 128)
{
int tag = 0;
while (pos >0)
{
pos--;
if (mask[pos] != 255)
{
tag = 1;
break;
}
}
if (tag == 0)return true;
else return false;
}
else return false;
}
else
return false;
}
int main()
{
string str;
int A, B, C, D, E, errIpMask, privateIP;
A = B = C = D = E = errIpMask = privateIP = 0;
while(getline(cin, str))
{
vector<string> tempSepStr, ipStr, maskStr;
vector<int> ip, mask;
splitString(str, "~", tempSepStr);
splitString(tempSepStr[0], ".", ipStr);
splitString(tempSepStr[1], ".", maskStr);
for (int i = 0; i < ipStr.size(); i++)
{
ip.push_back(stoi(ipStr[i]));
}
for (int i = 0; i < maskStr.size(); i++)
{
mask.push_back(stoi(maskStr[i]));
}
if (validMask(mask)&&checkIpNums(ip))
{
if (ip[0] >= 1 && ip[0] <= 126)
{
if (ip[0] == 10)privateIP++;
A++;
}
if (ip[0] == 172 && ip[1] >= 16 && ip[1] <= 31)privateIP++;
if (ip[0] == 192 && ip[1] == 168)privateIP++;
if (ip[0] >= 128 && ip[0] <= 191) B++;
if (ip[0] >= 192 && ip[0] <= 223) C++;
if (ip[0] >= 224 && ip[0] <= 239) D++;
if (ip[0] >= 240 && ip[0] <= 255) E++;
}
else
errIpMask++;
}
cout << A << " " << B << " " << C << " " << D << " " << E
<< " " << errIpMask << " " << privateIP << endl;
system("pause");
return 0;
}
18. 字符串匹配(华为机试)
判断短字符串中的所有字符是否在长字符串中全部出现
详细描述:
接口说明
原型:
boolIsAllCharExist(char* pShortString,char* pLongString);
输入参数:
char* pShortString:短字符串
char* pLongString:长字符串
输入描述:
输入两个字符串。第一个为短字符,第二个为长字符。
输出描述:
返回值:
示例1
输入
bc
abc
输出
true
#include <iostream>
#include <string>
#include <map>
using namespace std;
int main(){
string s1,s2,res;
while(cin >> s1 >> s2){
res="true";//注意此语句放在while语句内,因为每次循环都要重置res为true
int n1=s1.size(),n2=s2.size();
map<char,int> m;
for(int i=0;i<n2;i++){
m[s2[i]]=1;
}
for(int i=0;i<n1;i++){
if(m[s1[i]]!=1){
res="false";
break;
}
}
cout << res <<endl;
}
return 0;
}
19. 字符统计(华为机试)
如果统计的个数相同,则按照ASCII码由小到大排序输出 。如果有其他字符,则对这些字符不用进行统计。
实现以下接口:
输入一个字符串,对字符中的各个英文字符,数字,空格进行统计(可反复调用)
按照统计个数由多到少输出统计结果,如果统计的个数相同,则按照ASCII码由小到大排序输出
清空目前的统计结果,重新统计
调用者会保证:
输入的字符串以‘\0’结尾。
输入描述:
输入一串字符。
输出描述:
对字符中的
各个英文字符(大小写分开统计),数字,空格进行统计,并按照统计个数由多到少输出,如果统计的个数相同,则按照ASII码由小到大排序输出 。如果有其他字符,则对这些字符不用进行统计。
示例1
输入
aadddccddc
输出
dca
#include <iostream>
#include <unordered_map>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
bool comp(pair<char,int> a, pair<char,int> b) // 重载sort函数的自定义比较函数comp
{
if(a.second > b.second)
return true;
else if(a.second == b.second && a.first < b.first)
return true;
else return false;
}
int main()
{
string str;
unordered_map<char, int> countMap;
//unordered_map 主键是无序的,而map是按照主键的大小有序存储的。
while(getline(cin,str))
{
for(int i=0; i <= str.length()-1; i++)
{
//注意这种写法
if(countMap.find(str[i]) == countMap.end())
{
countMap[str[i]] = 1;
}
else
{
countMap[str[i]]++;
}
}
vector<pair<char, int>> elems(countMap.begin(), countMap.end());
sort(elems.begin(), elems.end(), comp); // 使用sort对hash表进行排序
//利用迭代器,不同的容器类定义了自己的iterator类型,用于访问容器内的元素。换句话说,每个容器定义了一种名为iterator的类型,而这种类型支持(概念上的)迭代器的各种行为。
for(vector<pair<char, int>>::iterator it=elems.begin(); it != elems.end(); it++)
{
cout<<it->first;
}
cout<<endl;
countMap.clear();
}
return 0;
}