剑指offer-难题记录

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013457167/article/details/82150897

将最近几天遇到的难题,整理记录下来,以备复习!
记录两个有趣的网址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,而大小王可以为任何数字。

扫描二维码关注公众号,回复: 3851732 查看本文章
思路

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解法

猜你喜欢

转载自blog.csdn.net/u013457167/article/details/82150897