笔试|面试真题

文章目录

  • 字符串模式匹配
  • 移除子串
  • n个人排名,允许并列名次,共有多少种排名结果?
  • 三个线程交替打印ali
  • 扔鸡蛋
    • 法一(笨蛋法)
    • 法二(二分法)
    • 法三(假设法)
  • 输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变
  • 把数组排成最小的数
  • 计算 n!阶乘的结果的末尾有几个0
  • 1堆n个石子每次最多取m个
  • 一个字符串中连续出现次数最多的子串
  • 给定一个无序整型数组,找出数组中未出现的最小正整数
  • 圆圈中剩下的最后一个数
  • 序列变为0
  • 给定两个字符串s和t,判断s是否是t的子序列
  • 最小覆盖子串
  • 猴子分桃
  • 缺少的最小正数
  • 无重复字符的最长子串
  • 旋转数组的最小值
  • 字符串转整数(atoi)
  • 用栈实现队列
  • 用队列实现栈
  • 汽车加油站问题
  • K个一组翻转链表
  • 删除驼峰式字符串

字符串模式匹配

问题描述:

有一个字符串它的构成是词+空格的组合,如“北京 杭州 杭州 北京”, 要求输入一个匹配模式(简单的以字符来写), 比如 a a b b aabb aabb, 来判断该字符串是否符合该模式。

举例:


1. pattern = "abba", str="北京 杭州 杭州 北京" 返回 true

2. pattern = "aabb", str="北京 杭州 杭州 北京" 返回 false

3. pattern = "baab", str="北京 杭州 杭州 北京" 返回 true

解题思路:

  • 对于 p a t t e r n pattern pattern,我们需要将其每一个字符都需要对应 s t r str str 中的一个词。

  • 从左到右同时遍历字符数组和单词数组,并在该过程中:

  • p a t t e r n pattern pattern 字符数组第一次出现某一字符时,根据其在单词数组中的位置,记录该字符对应的单词;

  • p a t t e r n pattern pattern 字符数组第 N N N 次( N > 1 N > 1 N>1 )出现某一字符时,取出该字符应该对应的单词,并与当前字符对应在单词数组中的单词对比,如果相同则继续校验,否则返回 f a l s e false false

  • 如果校验至最后一个单词仍然成立,则返回 t r u e true true

问题:如何知道字符是不是第一次出现呢? 如何记录某一字符所对应的单词呢?

  • 通过 u n o r d e r e d _ m a p unordered\_map unordered_map将字符和单词进行映射
  • 通过 f i n d find find函数可判断该字符(键)是否存在
  • 如果不存在则表示是第一次出现,我们需要建立字符和单词之间的映射关系
  • 否则,如果该字符已经存在,则需要和当前遍历到的单词与该字符已经建立映射关系的单词进行比较。
  • 如果两者不相等,返回 f a l s e false false
  • 否则,继续进行下一个单词的遍历
  • 直到当所有的单词比较完成,返回 t r u e true true

代码实现:

字符串分割的函数

vector<string> split(const string &str, const string &pattern)
{
	vector<string>  res;
	size_t start = 0, index = str.find_first_of(pattern, 0);
	while (index != str.npos)
	{
		if (start != index)
		{
			res.push_back(str.substr(start, index - start));
		}
		start = index + 1;
		index = str.find_first_of(pattern, start);
	}
	if (!str.substr(start).empty())
	{

		res.push_back(str.substr(start));
	}

	return res;
}

字符串匹配的函数

bool matchPattern(const string &str, const string &pattern)
{
	//首先将str进行拆分
	vector<string> words = split(str, " ");
	//判断分割完成之后的单词长度是否等于模式的长度
	if (words.size() != pattern.length())
	{
		return false;
	}
	//建立单词和拆分的单词之间的映射关系
	unordered_map<char, string>   map;
	//从左到右同时遍历字符数组和单词数组
	for (int i = 0; i < pattern.length(); ++i)
	{
        //如果字符是第一次出现则建立映射关系
		if (map.find(pattern[i]) == map.end())
		{
			map.insert(make_pair(pattern[i], words[i]));
		}
		// 否则直接进行比较
		else
		{
			if (map[pattern[i]] != words[i])
			{
				return false;
			}
		}
	}
	return true;
}

移除子串

问题描述:

给定一个字符串以及多个子串,对于在字符串中出现的子串可以多次移除,求多次移除后能够得到的最短字符串长度。
输入: 第一行为一个字符串,第二行为多个子串,字符串长度大于0
输出: 多次移除后能够得到的最短字符串长度

示例1:

输入:

"ccdaabcdbb"

["ab","cd"]

输出:

2

解释:
ccdaabcdbb -> ccdacdbb -> cabb -> cb (length = 2)

示例 2:

输入:

"abcabd"

["ab","abcd"]

输出:

0

解释:

abcabd -> abcd -> "" (length = 0)

————————
解题思路:

  • 每次取队列首元素作为要处理的主串
  • 在主串中找到每个子串(该子串有可能重复出现在主串中),将剔除子串之后的主串添加进set集合和queue队列中,并更新剔除子串之后主串的长度
  • 当队列为空时,表示所有的情况都处理完毕,返回最小的长度

代码实现:

int moveSubstr(string &s, unordered_set<string> &dict)
	{
		int size = s.size();
		if (size == 0)
		{
			return 0;
		}

		queue<string> q;//存放待处理的主串
		unordered_set<string> hashSet;//记录每种可能存在的主串的情况

		int minLen = size;//最小的长度
		q.push(s);//首先把原始主串添加进去
		hashSet.insert(s);

		while (!q.empty())
		{
			string s = q.front();//每次取队头元素作为待处理的主串
			q.pop();//从队列中删除,表示该主串将要被处理

            //遍历每个子串
			for (auto it = dict.begin(); it != dict.end(); it++)
			{
				string str = *it;
               //在主串中找子串,返回子串中第一个字符出现的位置
				int pos = s.find(str);
				while (pos != s.npos)//找到了
				{
				   //new_s表示剔除子串之后的主串
					string new_s = s.substr(0, pos) + s.substr(pos + str.size());
					//没有在hashSet中找到,表示该字符串没有被处理过
					if (hashSet.find(new_s) == hashSet.end())
					{
						q.push(new_s);//加入待处理队列
						hashSet.insert(new_s);//加入hashSet中,算是一种优化
						minLen = min(minLen, (int)new_s.size());//更新最小长度
		// 因为可能存在同一个子串在主串中多次出现,我们必须处理该子串在主串中不同位置的情况				
					pos = s.find(str, pos + 1);
				}
			}
		}
		return minLen;
	}

n个人排名,允许并列名次,共有多少种排名结果?

解题思路

  • d p [ i ] [ j ] dp[i][j] dp[i][j]:表示 i i i个人,名次个数为 j j j时,有多少种排名方法
  • j = = 1 j==1 j==1,表示 i i i个人,全部都是并列排名
    i > = j i>=j i>=j,表示有并列排名,也有独占一个名次的
  • 如果 i − 1 i-1 i1个人有 j j j个名次,那么如果第 i i i个人和前边的 i − 1 i-1 i1个人有并列排名
    d p [ i ] [ j ] = j ∗ d p [ i − 1 ] [ j ] dp[i][j]=j*dp[i-1][j] dp[i][j]=jdp[i1][j]
  • 但如果第 i i i个人和前边的 i − 1 i-1 i1个人都不并列,他独占其中一个名次
    d p [ i ] [ j ] = j ∗ ( d p [ i − 1 ] [ j − 1 ] ) dp[i][j]=j*(dp[i-1][j-1]) dp[i][j]=j(dp[i1][j1])

代码实现:

long long  order(long n)
{
	vector<vector<long long>>   dp(n + 1, vector<long long>(n + 1, 0));
	for (int i = 1; i <= n; ++i)
	{
		for (int j = 1; j <= i; ++j)
		{
			if (j == 1)
			{
				dp[i][j] = 1;//全部是并列排名
			}
			else
			{
			   //有并列排名,也有独占名次的
				dp[i][j] = j * (dp[i - 1][j] + dp[i - 1][j - 1]);
			}
		}
	}

   long long  sum = 0;
	for (int j = 1; j <= n; ++j)
	{
		sum += dp[n][j];//j的取值从1-n
	}
	return sum;
}

三个线程交替打印ali

题目描述:

三个线程交替打印alialiali…,一个打印a,一个打印l,一个打印i

解题思路:

加互斥锁

class ali {
private:
	int n;
	mutex m1, m2,m3;
public:
	ali(int n) {
		this->n = n;
		m2.lock();
		m3.lock();
	}

	void a(function<void()> printa) {

		for (int i = 0; i < n; i++) {
			m1.lock();//当第一次执行时,a会被首次执行,其后a的每一次执行,都在i之后
			printa();
			m2.unlock();
		}
	}

	void l(function<void()> printl) {

		for (int i = 0; i < n; i++) {

			m2.lock();//l的每一次执行,都在a之后
			printl();
			m3.unlock();
		}
	}
	void i(function<void()> printi) {

		for (int i = 0; i < n; i++) {

			m3.lock();//i的每次执行,都在l之后
			printi();
			m1.unlock();
		}
	}
};

扔鸡蛋

题目描述:

两个鸡蛋测试:
从100层楼往下扔鸡蛋,求最坏情况下确认保证鸡蛋可以不破的最大楼层所需次数

法一(笨蛋法)

  • 用一个鸡蛋,从第一层开始扔,一层不碎就再来一层
  • 这样做的代价就是最坏的话是需要做一百次

法二(二分法)

  • 把鸡蛋从一半楼层(50层)往下扔。
  • 如果第一枚鸡蛋在50层碎了,第二枚鸡蛋就从第1层开始扔,一层一层增长,一直扔到第49层。
  • 如果第一枚鸡蛋在50层没碎了,则继续使用二分法,在剩余楼层的一半(75层)往下扔…
  • 这个方法在最坏情况下,需要尝试50次。

法三(假设法)

我们假设这个方法是存在最优解的,也就是最少的尝试次数是x次

  • 首先我们要想一下,那么我们第一次尝试的楼层应该是多少层。

  • 备选答案是:x-1,x+1,x

  • 假设第一次扔在第x+1层

  • 如果第一个鸡蛋碎了,那么第二个鸡蛋只能从第1层开始一层一层扔,一直扔到第x层。这样一来,我们总共尝试了x+1次,和假设尝试x次相悖由此可见,第一次扔的楼层必须小于x+1层。

  • 假设第一次扔在第x-1层

  • 如果第一个鸡蛋碎了,那么第二个鸡蛋只能从第1层开始一层一层扔,一直扔到第x-2层。这样一来,我们总共尝试了x-2+1 = x-1次,诚然,这样的数据是符合标准的,但是我们的前提是在可能的情况下,让第一枚鸡蛋发挥它最大的光和热,显然,这还不够优秀,我们再试一下最后一个备选答案。

  • 假设第一次扔在第x层

  • 如果第一个鸡蛋碎了,那么第二个鸡蛋只能从第1层开始一层一层扔,一直扔到第x-1层。这样一来,我们总共尝试了x-1+1 = x次,刚刚好没有超出假设次数。

划重点了!因此,要想尽量楼层跨度大一些,又要保证不超过假设的尝试次数x,那么第一次扔鸡蛋的最优选择就是第x层。

  • 那么下一次的鸡蛋应该扔在什么地方呢?

  • 如果第一次的蛋蛋没有牺牲,我们的尝试次数就少了一次,同时我们也刷掉了X个错误的楼层,这时候问题就转化为了:我们有两个鸡蛋,在100-x层楼往下扔,要求尝试次数不超过x-1次。

  • 这个时候,我们的尝试上限变成了 ( x − 1 ) (x-1) (x1)次,由上面的尝试可以知道,这次我们的选择应该是 ( 100 − x ) (100-x) (100x)里面的第 ( x − 1 ) (x-1) (x1)层,即为 ( x + x − 1 ) (x+x-1) (x+x1)层。

  • 同理,只要是蛋蛋一直坚挺不碎的话,那么接下来就是 ( x − 2 ) (x-2) (x2), ( x − 3 ) (x-3) (x3)

  • x + ( x − 1 ) + ( x − 2 ) + … + 1 = 100 x + (x-1) + (x-2) + … + 1 = 100 x+(x1)+(x2)++1=100

  • 因为咱们尝试了x次呀,所以左边不就应该有x个项相加嘛,而右边就是层数100了。

  (x+1)x/2 = 100    (首项+尾项)*项数/2=100   
  最终x向上取整,得到 x = 14

代码实现:
时间复杂度为 O ( n 2 ∗ m ) O(n^2*m) O(n2m)

const int N = 110, M = 11;
int n, m;
int f[N][M];

int main()
{
	while (cin >> n >> m)
	{
	     // f[i][j]表示测量区间为i,且有j个鸡蛋的测量方案
		for (int i = 1; i <= n; i++) f[i][1] = i;
		for (int i = 1; i <= m; i++) f[1][i] = 1;

		for (int i = 2; i <= n; i++)
		{
			for (int j = 2; j <= m; j++)
			{
			   //情况1:没有用过鸡蛋j,说明在测量区间长度为i时,使用了j-1个鸡蛋
				f[i][j] = f[i][j - 1]; 
				
				//情况2:用了鸡蛋j,需要知道在第几层用了
				for (int k = 1; k <= i; k++)
				{
 /* 假设在k处进行了测量,又分为两种情况
 * 1. 在k处鸡蛋碎了,则测量区间变为1~k-1,区间长度为k-1,鸡蛋剩余j-1个
 * 2. 在k处鸡蛋没碎,测量区间为k+1~i,区间长度为i-k,鸡蛋剩余j个
 * 由于我们可以控制鸡蛋在哪个位置去测,没办法控制测量的结果
 * 对于我们无法控制的结果,需要取最坏的情况  max(f[k - 1][j - 1], f[i - k][j])            
 */
					f[i][j] = min(f[i][j], max(f[k - 1][j - 1], f[i - k][j]) + 1);
				}
			}
		}
		cout << f[n][m] << endl;
	}
	return 0;
}


输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变

句子中单词以空格符隔开。

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;

int main()
{
	string str;
	getline(cin ,str);
	reverse(str.begin(), str.end());//首先对句子整体进行翻转
	vector<string>  vec;
	size_t  pos = str.find(' ');
	while (pos != std::string::npos)//将输入的字符串以空格进行分割
	{
		vec.push_back(str.substr(0, pos));
		str = str.substr(pos+1);
		pos = str.find(' ');
	}
	vec.push_back(str);
	for (int i = 0; i < vec.size(); ++i)
	{
		reverse(vec[i].begin(), vec[i].end());//对单词进行翻转
	}
	for (string str : vec)
	{
		cout << str << " ";
	}
}

把数组排成最小的数

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;

bool cmp(string str1, string str2)//自定义比较规则
{
	return str1 + str2 > str2 + str1;
}
int main()
{
	string str;
	getline(cin ,str);
	vector<string>  vec;
	size_t  pos = str.find(',');
	while (pos != std::string::npos)//将输入的字符串以‘,’进行分割
	{
		vec.push_back(str.substr(0, pos));
		str = str.substr(pos+1);
		pos = str.find(',');
	}
	vec.push_back(str);
	sort(vec.begin(), vec.end(), cmp);
	for (string str : vec)
	{
		cout << str;
	}
}

计算 n!阶乘的结果的末尾有几个0

首先,我们想什么情况下会产生一个0?

  • 一个数乘以 10,在末尾就会多出一个 0。而 10 = 5 * 2。

  • 一组数相乘的结果末尾有几个0,取决于这组数因式分解后有几对 5 和 2 的因子。

  • 针对于 n! 这个题目,有这样一个事实:把相乘的数因式分解后,2 的个数肯定大于 5 的个数。

  • 所以,这个问题可以拆解为:只要求出因式分解后有几个 5 的因子即可,5的个数即是末尾出现的0的个数。


#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;

int main()
{
	int n;
	cin >> n;
	int count = 0;
	while (n)
	{
		count += n / 5;
		n /= 5;
	}
	cout << count << endl;
}

1堆n个石子每次最多取m个

  • 如果n=m+1,那么由于一次最多只能取m个,所以,无论先取者拿走多少个,后取者都能够一次拿走剩余的物品,后者取胜。

  • 因此我们发现了如何取胜的法则:如果n=(m+1)×r+s,(r为任意自然数,s≤m),

  • 那么先取者要拿走s个物品,如果后取者拿走k(≤m)个,那么先取者再拿走m+1-k个,结果剩下(m+1)(r-1)个,以后保持这样的取法,那么先取者肯定获胜。

  • 总之,要保持给对手留下(m+1)的倍数,就能最后获胜。

  • 若n=k*(m+1),则后取着胜,反之,存在先取者获胜的取法。

一个字符串中连续出现次数最多的子串

解题思路:

  • 利用后缀数组后缀数组是一种数据结构,对一个字符串生成相应的后缀数组后,然后再排序,排完序依次检测相邻的两个字符串的开头公共部分。
  • 时间复杂度为:总的时间复杂度是 O ( N 2 ∗ l o g N ) O(N^2*logN) O(N2logN)

后缀数组,每一行比上一行少一个,跳行就可出现少2个、3个……

  • 第一趟:(一个长度的子串)第一行a与第二行第一个b比较是否相等,不等 (若相等则继续在第二行后取长度为1的子串比较,碰到不等为止)
  • (两个长度的子串)第一行ab与第三行首位两个ab比较,相等,继续在第三行取长度为2的子串比较,碰到c不够终止。。
  • (三个长度的子串)以此类推···········
abababc  
bababc
ababc
babc
abc
bc
c
pair<int, string> fun(const string &str)
{
	vector<string> substrs;
	int len = str.length();
 
	string substring;
	int maxcount(0);
	//后缀数组
	cout << "the string is:" << str << endl;
	cout << "the substrings are as follows:" << endl;
	for (int i = 0; i < len; ++i)
	{
		substrs.push_back(str.substr(i));
		cout << substrs[i] << endl;
	}
	
	cout << "--------------the answer------------" << endl;
	
	for (int i = 0; i < len; ++i)
	{
		for (int j = i + 1; j < len; ++j)
		{
			int count = 1;
			int sublen = j - i;
 
			int lens = substrs[j].length();
 
			for (int k = 0; k < lens; k += sublen)
			{
				if (substrs[i].substr(0,sublen) == substrs[j].substr(k,sublen))
				{
					++count;
				}
				else 
				{
					break;
				}
			}
			//比较最大连续出现次数,并记录相应的子串
			if (count>maxcount)
			{
				maxcount = count;
				substring = substrs[i].substr(0, sublen);
			}
		}
	}
 
	return make_pair(maxcount, substring);
 
}
 
 
 
int main()
{
	string str = "";
	auto res = fun(str);
 
	cout << "the max count is:" << res.first << endl;
	cout << "the matched substring is:" << res.second << endl;
}

给定一个无序整型数组,找出数组中未出现的最小正整数

题意的限定是正整数,所以可以以0为边界,只考虑0以上的数,因此结果要么在1~n之间,要么是n+1。我们的方法是以数组的下标为标准来比较

  • 假如所有的i==ves[i]除了有一个j!=ves[j],那么j就是要找的数。
  • 除了0处的值之外,其他的值都满足i==ves[i],则结果与0处的值有关系。
  • 再假设一种极端情况,也就是所有的值都比n大或者比0小,那么结果就是1,而值在0~n之间的则把他们进行归位,让i和ves[i]相等,这样做就是为了找到第一个不满足i==ves[i]的值就是要找的结果。
  • 注意,归位的方法是进行数组值的交换。满足条件i==ves[i],或ves[ves[i]]==ves[i],背后的意义就是交换或者被交换的值满足序号和值相等的条件。

#include<iostream>
#include<vector>
 
using namespace std;
 
int main()
{
	int n,result=0;
	cin >> n;
	int temp,t;
	vector<int> ves;
	for (int i = 0; i < n; i++) {
 
		cin >> temp;
		ves.push_back(temp);
	}
	for (int i = n-1;i>=0; i--) {
 
		while (0 < ves[i] && ves[i] < n&&i != ves[i]) {
			t = ves[i];
			if (ves[t] == ves[i]) {
				break;
			}
			swap(ves[i], ves[t]);//归位操作
		}
	}
	for (int i = 1; i < n; i++) {
		if (ves[i] != i) {
			result=i;
			break;
		}
	}
	if (result == 0) {//其他都归位了,就取决于0处的值
        result=n + (ves[0] == n);
	}
	cout << result << endl;
	return 0;
}

圆圈中剩下的最后一个数

class Solution {
public:
    int LastRemaining_Solution(int n, int m)
    {
       if(n<1||m<1)
       {
           return -1;
       }
        list<int> list;
        for(int i=0;i<n;++i)
        {
            list.push_back(i);
        }
        auto it=list.begin();
        while(list.size()>1)
        {
            for(int i=1;i<m;++i)//走m-1步
            {
                it=(++it==list.end()?list.begin():it);
            }
            auto del=it;//将要剔除掉的数
            auto next=(++it==list.end()?list.begin():it);//保存下一个要开始报数的人
            list.erase(del);
            it=next;
        }
        return list.back();
    }
};



public static int LastRemaining_Solution3(int n, int m) {
        if(n == 0 || m <= 0) 
            return -1;
        
        int last = 0;
        for(int i = 2; i <= n; i++) {
            last = (last + m) % i;
            }
        }
        

序列变为0

题目描述:

给定正整数N,你的任务是用最少的操作次数把1,2,3…N中所有数变为0
每次操作可从序列中选任意个整数,同时减去一个个相同的正整数。
例如:1,2,3可以同时把后两个减去2,得到1,0,1,然后再选择第一个和第三个,同时减去1即可

题目分析:

例子中,当n=3时,对数列1,2,3,把2和3同时减去2得到1,0,1,然后再选择第一个和第三个,同时减去1即可。类似的,比如当n=7时,对数列1,2,3,4,5,6,7,把4,5,6,7同时减去4得到1,2,3,0,1,2,3,此时问题等价于n=3时的情况。
令f(n)表示最少的操作次数,则有f(1)=1,f(2)=2,f(3)=2,f(4)=3,f(5)=3,f(6)=3,f(7)=3,f(8)=4,f(9)=4…通过以上规律可知,当
2 m − 1 2^{m-1} 2m1 <=n < 2 m 2^m 2m时,f(n)=m.

代码实现:

  int main()
  {
    int n;
	cin >> n;
	int sum = 0;
	for (; n > 0; n /= 2)
	{
		sum++;
	}
	cout << sum;
 }

给定两个字符串s和t,判断s是否是t的子序列

题目描述

输入两个字符串s和t,判断是否可以从t中删除0个或者多个字符(顺序不变),得到字符串s,比如abcde可以得到bde,但不能得到ed。

代码实现:

bool find_string(const char *src, const char *dst)
{
	while (*src)
	{
		if (*src == *dst)
		{
			dst++;
			if (!(*dst))
			{
				return 1;
			}
		}
		src++;
	}
	return 0;

}

最小覆盖子串

题目描述

给定一个字符串 S 和一个字符串 T,请在 S 中找出包含 T 所有字母的最小子串。

举例

输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"

解题思路:
哈希表记录字符串T中的值,滑动窗口法遍历字符串S寻找最小子串

  1. 首先用一个map记录T中所有字母出现的次数
  2. 设置字符串s的索引指针left和right,[left, right]为我们所设置的滑动窗口。
  3. 当left索引小于s.length()时,重复以下循环:
    a.判断当前字符串s中[left, right]是否包含了T中的所有字母。
    b.如果是,那么就应该向右移动left指针尝试着缩小[left, right]中所包含的内容。
    c.如果不是,且right + 1 < s.length(),则表明right指针能够向右移动。
    d.每一轮循环中都要判断一次当前经过b.c.两步变动后的[left, right]中所包含的内容是否包含了T中的所有字母。如果是,则需要记录该子串的长度并与minlen变量比较,如果目前子串的长度小于minlen,则更新minlen变量的值为目前子串的长度,且将[left, right]所对应的子串保存在res中。
  4. 循环结束以后,如果minlen== INT_MAX则说明在s中并没有找到满足条件的字符串,返回"";否则,返回记录的最小子串res。

如何判断s[left, right]范围的字符串包含了t中的多少个字符?
5. 在进入循环前就设置一个初值为0的count变量,用以记录s中[left, right]范围的字符串包含了t中的多少个字符。

  1. 在循环里,如果count < t.length()则说明s中[left, right]范围的字符串还没有包含t中所有字母。right指针需要向右移动。同时移动之前也要先判断是否满足right + 1 < s.length()。

  2. 接着判断s[right]是否是字符串t中的字符,每发现t中的一个字符,相应的map中的值就需要减1。

  3. 如果map中s[right]所对应字符的数量大于0,则说明t中的该字符还有剩余数量,并没有完全被s中[left, right]范围的字符串所包含,此时需要count++,同时map中对应的键的值需要-1。

  4. 如果map中s[right]所对应的字符的数量已经小于等于0了,那么说明s中[left, right]范围的字符串包含了超出t中所包含个数的相应字符,此时count不应该+1,但是map中对应的键的值仍然需要-1,表示s中[left, right]范围的字符串包含了多余的相应字符。

  5. 如果count >= t.length()则说明s中[left, right]范围的字符串已经包含了t中所有字母。left指针需要向右移动。

  6. 移动left指针之前,我们需要判断当前的left指针所指的s中的字符是否是t中的字符。

  7. 如果是,接着判断map中所对应的字符的数量是否等于0,如果等于0,则说明s中[left, right]范围的字符串所包含该字符的数量与t中所包含的数量相等,left右移缺失这个字符后,s中[left, right]范围的字符串所包含的t中的字符的数量会减少1,需要count–,同时相应的map中的值需要+1。如果map中所对应的字符的数量小于0,表示s中[left, right]范围的字符串包含了富余的相应字符,此时left右移缺失的这个字符不会导致count–,只需要map中相应的值+1即可。


代码实现:

string minWindow(string s, string t)
{
	string res = "";
	unordered_map<char, int> map;
	for (int i = 0; i < t.length(); ++i)//保存t中字符出现的次数
	{
		++map[t[i]];
	}
	int left = 0;//滑动窗口的左边界
	int right = 0;//滑动窗口的右边界
	int minlen = INT_MAX;
	int count = 0;
	
	for (; right < s.length(); ++right)
	{
		if (--map[s[right]] >= 0)//s中包含t中的字符
		{
			count++;
		}
		while(count == t.length())//找到一个包含t中年所有字符的区间
		{
			if (minlen > right - left + 1)
			{
				minlen = right - left + 1;
				res = s.substr(left, minlen);
			}
			if (++map[s[left]] > 0)//在s中剔除掉该字符之后会使得该区间不包含t中的该字符
			{
				count--;
			}
			++left;
		}
	}
}

猴子分桃

老猴子辛苦了一辈子,给那群小猴子们留下了一笔巨大的财富——一大堆桃子。老猴子决定把这些桃子分给小猴子。
第一个猴子来了,它把桃子分成五堆,五堆一样多,但还多出一个。它把剩下的一个留给老猴子,自己拿走其中的一堆。
第二个猴子来了,它把桃子分成五堆,五堆一样多,但又多出一个。它把多出的一个留给老猴子,自己拿走其中的一堆。
后来的小猴子都如此照办。最后剩下的桃子全部留给老猴子。
这里有5只小猴子,请你写个程序计算一下在开始时至少有多少个桃子,以及最后老猴子最少能得到几个桃子。

int main()
{
	int num = 1;//第5只猴子拿走的桃子数
	int total = 0;//每个猴子看到的桃子数
	
	bool  checktimes = false;

	while (!checktimes)
	{
		int count = 0;
		for (int i = 5; i >= 1; i--)
		{
			if (i == 5)  //第5只猴子看到的桃子数是以它拿走的桃子数来计算的
			{
				total = num * 5 + 1;
			}
			else
			{
				total = total * 5 / 4 + 1;//其他的猴子看到的桃子数都是下一个猴子看到的桃子树*5/4+1
			}
			if (total % 4 != 0)//从第2-5只猴子看到的桃子数应该是4的倍数
			{
				break;
			}
			else
			{
				count++;//如果满足条件(从第2-5只猴子看到的桃子数应该是4的倍数),就count++;
			}
		}

		if(count==4)//说明从第2-5只猴子看到的桃子数都满足条件
		{ 
			cout << total;
			checktimes = true;
		}
		else
		{
			num++;
		}
	}
	

	
}

缺少的最小正数

题目描述

给你一个未排序的整数数组,请你找出其中没有出现的最小的正整数。

代码实现:

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        int n=nums.size();

        for(int i=0;i<n;++i)
        {
            while(nums[i]>0&&nums[i]<n&&nums[i]!=nums[nums[i]-1])
            {
                swap(nums[i],nums[nums[i]-1]);
            }
            
        }
        for(int i=0;i<n;++i)
        {
            if(nums[i]!=i+1)
            {
                 return i+1;            
            }
        }
        return n+1;
    }
};

无重复字符的最长子串

int  lengthOfLongestSubstring(string s)
{
	int j = 0;
	vector<char> vec(128);

	int maxLen = INT_MIN;
	for (int i = 0; i < s.size(); ++i)
	{
		vec[s[i]]++;
		while (vec[s[i]] > 1)
		{
			vec[s[j]]--;
			j++;
		}
		maxLen = max(maxLen, i - j + 1);
	}
	return maxLen;

}

旋转数组的最小值

class Solution {
public:
    int minArray(vector<int>& numbers) {
        int left=0;
        int right=numbers.size()-1;
        while(left<right)
        {
            int mid=(left+right)>>1;
            //中间值在前半部分递增数组
            if(numbers[mid]>numbers[right])
            {
                left=mid+1;
            }
            //中间值在后半部分递增数组
            else if(numbers[mid]<numbers[right])
            {
                right=mid;
            }
            else
            {
                right--;
            }

        }
        return numbers[left];
    }
};

字符串转整数(atoi)

 class Solution {
public:
    int myAtoi(string str) {
        int len=str.length();
        int flag=1;
        int i=0;
        int res=0;
        while(i<len)
        {
            while(i<len&&str[i]==' ')//去掉前导空格
            {
                i++;
            }
            if(str[i]!='+'&&str[i]!='-'&&!isdigit(str[i]))
            {
                return 0;
            }
            if(str[i]=='+'||str[i]=='-')
            {
                flag=str[i]=='+'?1:-1;
                i++;
            }
            if(i<len)
            {
                while(i<len&&isdigit(str[i]))
                {
                   int digit=str[i]-'0';
                   if(res>(INT_MAX-digit)/10)//防止溢出
                   {
                       return flag==1?INT_MAX:INT_MIN;
                   }
                   res=res*10+digit;
                   i++;
                }
                return res*flag;
            }

        }
        return res*flag;
    }
};

用栈实现队列

class MyQueue {
public:
    stack<int> pushs,pops;
    /** Initialize your data structure here. */
    MyQueue() {

    }

    void fun()
    {
        if(pops.empty())
        {
            while(!pushs.empty())
            {
                pops.push(pushs.top());
                pushs.pop();
            }
        }
    }

    
    /** Push element x to the back of queue. */
    void push(int x) {
        pushs.push(x);
    }
    
    /** Removes the element from in front of queue and returns that element. */
    int pop() {
        fun();
        int res=pops.top();
        pops.pop();
        return res;

    }
    
    /** Get the front element. */
    int peek() {
        fun();
        return pops.top();
    }
    
    /** Returns whether the queue is empty. */
    bool empty() {
        return pops.empty()&&pushs.empty();
    }
};

/**
 * Your MyQueue object will be instantiated and called as such:
 * MyQueue* obj = new MyQueue();
 * obj->push(x);
 * int param_2 = obj->pop();
 * int param_3 = obj->peek();
 * bool param_4 = obj->empty();
 */

用队列实现栈

class MyStack {
public:
    queue<int> que;
    /** Initialize your data structure here. */
    MyStack() {

    }
    
    /** Push element x onto stack. */
    void push(int x) {
        que.push(x);
        //将队列原来的元素重新入队
        for(int i=0;i<que.size()-1;++i)
        {
            que.push(que.front());
            que.pop();
        }

    }
    
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        int top=que.front();
        que.pop();
        return top;
    }
    
    /** Get the top element. */
    int top() {
        return que.front();
    }
    
    /** Returns whether the stack is empty. */
    bool empty() {
        return que.empty();
    }
};

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack* obj = new MyStack();
 * obj->push(x);
 * int param_2 = obj->pop();
 * int param_3 = obj->top();
 * bool param_4 = obj->empty();
 */

汽车加油站问题

int Times(int n, int k, vector<int>& vec)
{
	int count = 0;
	int total = 0;
	for (int i = 0; i < vec.size(); ++i)
	{
		if (vec[i] > n)
		{
			//cout << "不可达";
			return -1;;
		}
		total += vec[i];
		if (total > n)
		{
			count++;
			total = 0;
			i--;
		}

	}
	return count;

}

K个一组翻转链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *reverse(ListNode *list)
    {
        ListNode *pre=nullptr;
        while(list)
        {
            ListNode *next=list->next;
            list->next=pre;
            pre=list;
            list=next;
        }
        return pre;
    }
    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode *dummy=new ListNode(-1);
        dummy->next=head;
        ListNode *pre=dummy;//待翻转链表的前驱节点
        ListNode *end=dummy;//待翻转链表的尾节点
        while(end->next)//保证至少有一个节点
        {
            for(int i=0;i<k&&end!=nullptr;++i)
            {
                end=end->next;
            }
            if(end==nullptr)
            {
                return dummy->next;
            }
            else
            {
               ListNode *next=end->next;//保存未翻转链表的头节点
               end->next=nullptr;//断开
               ListNode *start=pre->next;//待翻转链表的头节点
               pre->next=reverse(start);//连接前边
               start->next=next;//连接后边
                pre=start;//更新前驱节点
                end=pre;
            }
        }
        return dummy->next;
    }
};

删除驼峰式字符串

//AaABbB
//AaABbBbb
//AaABbBbBbcbcQv
bool fun(char ch1, char ch2)
{
	return (isupper(ch1) && islower(ch2)) || (islower(ch1) && isupper(ch2));
}
string deletestr(string& str)
{
	vector<pair<int, int>>  vec;
	int len = str.length();
	int i = 0;
	int j = 0;
	int count = -1;
	while (i < len - 2)
	{
		if (str[i] == str[i + 2] && fun(str[i], str[i + 1]))
		{
			vec.push_back(make_pair(i, i + 2));
			count++;
			//j = i + 2;
			i++;
			while (i <= vec[count].second)
			{
				if (i + 2 <= len - 1 && str[i] == str[i + 2] && fun(str[i], str[i + 1]))
				{
					vec[count].second = i + 2;
				}
				i++;
			}
			i = vec[count].second + 1;
		}
		else
		{
			i++;
		}
		
	}
	vector<bool> tmp(len,true);
	string res;
	for (int i = 0; i < vec.size(); ++i)
	{
		int first = vec[i].first;
		int end = vec[i].second;
		for (int k = first; k <= end; ++k)
		{
			tmp[k] = false;
		}
	}
	for (int i = 0; i < len; ++i)
	{
		if (tmp[i])
		{
			res += str[i];
		}
	}
	return res;
}

你可能感兴趣的:(笔试真题)