将最近几天遇到的难题,整理记录下来,以备复习!
记录两个有趣的网址CSDN排行第一博客和左耳朵陈浩关于fork
一、正则表达式匹配
本文参考:正则表达式
题目描述
请实现一个函数用来匹配包括’.’和’‘的正则表达式。模式中的字符’.’表示任意一个字符,而’‘表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串”aaa”与模式”a.a”和”ab*ac*a”匹配,但是与”aa.a”和”ab*a”均不匹配。
思路
A. 模式串下一个字符为*
,即(pattern + 1) == ‘‘:
1) 如果当前字符匹配,即*str = *pattern或者*str = ‘.’ && *pattern != ‘\0’,三种可能:
1、模式串当前字符出现0次,即*表示当前字符出现0次,
则str = str, pattern = pattern + 2;
2、模式串当前字符出现1次,即*表示当前字符出现1次,
则str = str + 1, pattern = pattern + 2;
3、模式串当前字符出现2次或2次以上,即*表示当前字符出现2次或以上,
则str = str + 1, pattern = pattern;
2)如果当前字符不匹配,则只能让*表示当前字符出现0次,则str = str, pattern = pattern + 2;
B.模式串下一个字符不为*
如果当前字符匹配,即*str = *pattern或者*str = ‘.’ && *pattern != ‘\0’,则str = str + 1, pattern = pattern + 1.
代码
bool matchCore(char* str, char* pattern)
{
if (*str == '\0' && *pattern == '\0')
return true;
if (*str != '\0' && *pattern == '\0')
return false;
if (*(pattern + 1) == '*')
{
if (*str == *pattern || (*pattern == '.' && *str != '\0'))
{
return matchCore(str, pattern + 2) || matchCore(str + 1, pattern + 2) || matchCore(str + 1, pattern);
}
else
{
// ignore *
return matchCore(str, pattern + 2);
}
}
if (*str == *pattern || (*pattern == '.' && *str != '\0'))
{
return matchCore(str + 1, pattern + 1);
}
return false;
}
bool match(char* str, char* pattern)
{
if (str == NULL || pattern == NULL) return false;
return matchCore(str, pattern);
}
二、滑动窗口的最大值
题目
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}。
针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}
代码
vector<int> maxInWindows(const vector<int>& num, unsigned int size)
{
vector<int> res;
if (num.empty() || size < 1) return res;
deque<int> deq;
for (int i = 0; i < num.size();++i)
{
//从后面依次弹出比当前元素小的值,保证队列首元素为当前窗口最大值的下标
while (deq.size() && num[deq.back()] < num[i])
{
deq.pop_back();
}
//当当前窗口的队首索引失效时将其移除
while (deq.size() && i-deq.front()+1 > size)
{
deq.pop_front();
}
//插入元素的索引(对应值为当前窗口最大值)
deq.push_back(i);
//当窗口首地址i>=size时,才寻找窗口最大值
if (size && i + 1 >= size)
res.push_back(num[deq.front()]);
}
return res;
}
vector<int> num{ 2,3,4,2,6,2,5,1 };
vector<int> res = maxInWindows(num, 3);
for (auto it = res.begin(); it != res.end(); ++it)
cout << *it << " ";
cout << endl;
三、扑克牌顺子
本文参考了扑克牌顺子
题目
从扑克牌中随机抽取5张,判断是不是一个顺子。2-10为数字本身,A为1,J为11,Q为12,K为13,而大小王可以为任何数字。
思路
1.关键思想就是把大小王看成0,而0又可以当做任何数字
2.首先判断出5个牌里面0的个数
3.判断出相邻数字之间的间隔,若间隔数大于0的个数,那么0就无法填充使之成为顺子,例如0,1,4,5,6,其中1到4的间隔差了4-1-1=2,而1个0是无法进行填充的,组成不了顺子
4.判断过程中若出现两个非零数字相等,那么绝不可能为顺子
代码
bool IsContinuous( vector<int> numbers ) {
int len = numbers.size();
if(len < 5) return false;
sort(numbers.begin(),numbers.end());
int numOfZero = 0;
int numOfGap = 0;
int start = 0;
while(numbers[start] ==0)
{
numOfZero++;
start++;
}
//start: the first non-zero
int end = start+1; //the second non-zero
while(end<len)
{
if(numbers[end] == numbers[start]) return false;
numOfGap += (numbers[end]-numbers[start]-1);
start = end;
end++;
}
//cout<<numOfGap<<" "<<numOfZero<<endl;
return (numOfGap >numOfZero)?false:true;
}
四、整数中1出现的次数(从1到n整数中1出现的次数)
题目:
输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。例如输入12,从1到12这些整数中包含1的数字有1,10,11和12,1一共出现了5次。
思路
解法一:不考虑时间效率的解法,时间复杂度O(nlogn)。
int NumberOf1(int num)
{
int count = 0;
while(num)
{
if(num%10 == 1)
count++;
num /= 10;
}
return count;
}
int NumberOf1Between1AndN_Solution(int n)
{
if(n<=0) return 0;
int sum = 0;
for (int i = 1; i<=n; i++)
sum+=NumberOf1(i);
return sum;
}
解法二:
参考:《程序员代码面试指南》
int NumberOf1Between1AndN_Solution(int n)
{
if (n < 1) return 0;
int len = getLenOfNum(n);
if (n == 1) return 1; //1~9之间的数 f(n) = 1
int tmp = (int)pow(10, len - 1);
int first = n / tmp; //最高位
int firstOneNum = first == 1 ? n%tmp + 1 : tmp;
int otherOneNum = first * (len - 1) * (tmp / 10); //first*表示继续分段
return firstOneNum + otherOneNum + NumberOf1Between1AndN_Solution(n% tmp);
}
解法三:
参考 LeetCode解法