柠檬微趣C++笔试复盘

本文回顾了一次C++笔试,重点讨论了如何找到二叉树最浅的叶子结点以及实现正则表达式的匹配算法。对于二叉树问题,采用宽度优先搜索策略获取最浅叶子结点;在正则表达式匹配中,通过状态转移方程处理'*'和'?'两种特殊字符的情况,实现了匹配算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

柠檬微趣C++笔试复盘

有些题目是经典题目改版后的题,没有原题了,就只回忆记录下当时的做法。
1.二叉树的最浅叶子结点

  • 使用宽度优先搜索来进行遍历,宽搜到的叶子结点一定是距离最浅的叶子结点。然后把这一层的叶子结点全部取出来就得到了所有最浅的叶子结点的和。
    利用两个指针front和last来维护每一层的所有节点。
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <malloc.h>
 
#define maxlen 30
 
typedef struct Node
{
    struct Node * leftChild;
    struct Node * rightChild;
    int key;
}Node;
 
Node *initTree()
{
    int n = 0;
    scanf("%d",&n);
    if(0 == n){
        return NULL;
    }
    Node *p = (Node *)malloc(sizeof(Node));
    p->key = n;
    p->leftChild = initTree();
    p->rightChild = initTree();
 
    return p;
}
 
void preOrderRecursive(Node *root)
{
    if(root){
        printf("%d\n",root->key);
        preOrderRecursive(root->leftChild);
        preOrderRecursive(root->rightChild);
    }
}
 
int getResult(Node *pTree)
{
    Node *queue[maxlen];
    int front = 0;
    int rear = 0;
 
    int count = 0;
 
    queue[(++rear)%maxlen] = pTree;
    int last = 1;
    while(front != rear){
        Node *tmp = queue[(++front)%maxlen];
 		
        if(tmp->leftChild == NULL && tmp->rightChild == NULL){
            count += tmp->key;
            while(front != last){
                tmp = queue[(++front)%maxlen];
                if(tmp->leftChild == NULL && tmp->rightChild == NULL){
                    count += tmp->key;
                }
            }
            return count;
        }
        if(tmp->leftChild){
            queue[(++rear)%maxlen] = tmp->leftChild;
        }
        if(tmp->rightChild){
            queue[(++rear)%maxlen] = tmp->rightChild;
        }
 
        if(front == last){
            last = rear;
        }
    }
    return count;
}
/*
输入的先序遍历序列为:
1 5 22 0 0 14 0 31 0 0 9 8 0 98 0 0 7 0 0
*/
int main()
{
    Node *root = initTree();
    printf("先序遍历结果:\n");
    preOrderRecursive(root);
    printf("result = %d\n",getResult(root));
}

题目中给出了基本的输入输出和数据结构,自己只需要补足一个getReuslt()函数就可以。
2.实现简单的正则表达式
-这个力扣上有一个原题,不过这个题是 相对于力扣上的题多了一个条件:
'?‘表示可以匹配前一个字符1到n次,和’*‘不同的是,’*'表示可以匹配前一个字符0到n次。

状态转移方程如下:

如果p[j]!='*'&&p[j]!='?'
f[i][j]=f[i-1][j-1]&&(s[i]==p[j]||p[j]=='.')
如果p[j]=='*'
先放结论:
	f[i][j]=f[i][j-2]||i&&f[i-1][j]&&(s[i]==p[j-1]||p[j-1]=='.'
这里前面的比较容易理解,当'*'直接表示0个字符时,f[i][j]=f[i][j-2]
后面的这里不是特别容易理解,很多题解省略了一步推导
当'*'表示一个字符时, f[i][j]=f[i-1][j-2]&&s[i]==p[j-1]
当'*'表示两个字符时, f[i][j]=f[i-2][j-2]&&s[i-1]=s[i]=p[j-1]
当'*'表示三个字符时,f[i][j]=f[i-3][j-2]&&s[i-2]=s[i-1]=s[i]=p[j-2]
...
依次类推,我们是要枚举'*'可以表示几个状态吗?
这样的话,时间复杂度是o(n^3)的
我们不妨写出f[i-1][j]来观察规律:
当'*'表示0个字符的时候,f[i-1][j]=f[i-1][j-2]
当'*'表示一个字符时,f[i-1][j]=f[i-2][j-2]&&s[i-1]=p[j-1]
当'*'表示两个字符时,f[i-1][j]=f[i-3][j-2]&&s[i-2]=s[i-1]=p[j-1]

观察上述式子,可以看到f[i][j]可以由f[i-1][j]巧妙的转换
由此得出	f[i][j]=f[i][j-2]||i&&f[i-1][j]&&(s[i]==p[j-1]||p[j-1]=='.'

如果p[j]=='?'
'?'的意思与'*'的不同是'*'能表示0个字符,而'?'至少表示1个字符
按照上面的思路可以写出以下:
当'?'表示一个字符时, f[i][j]=f[i-1][j-2]&&s[i]==p[j-1]
当'?'表示两个字符时, f[i][j]=f[i-2][j-2]&&s[i-1]=s[i]=p[j-1]
当'?'表示三个字符时,f[i][j]=f[i-3][j-2]&&s[i-2]=s[i-1]=s[i]=p[j-2]

我们不妨写出f[i-1][j]来观察规律:
 当'?'表示一个字符时,f[i-1][j]=f[i-2][j-2]&&s[i-1]=p[j-1]
当'?'表示两个字符时,f[i-1][j]=f[i-3][j-2]&&s[i-2]=s[i-1]=p[j-1]
当'?'表示三个字符时,f[i-1][j]=f[i-4][j-2]&&s[i-3]=s[i-2]=s[i-2]=p[j-1]
由此得出结论,用f[i-1][j]代替后面的一堆东西得到:
f[i][j]=f[i-1][j]||(i&&f[i-1][j-2]&&(s[i]==p[j-1]||p[j-1]=='.'));

总的代码如下:

class Solution {
public:
    bool isMatch(string s, string p) {
        int n = s.size(), m = p.size();
        s = ' ' + s, p = ' ' + p;
        vector<vector<bool>> f(n + 1, vector<bool>(m + 1));
        f[0][0] = true;
        for (int i = 0; i <= n; i ++ )
            for (int j = 1; j <= m; j ++ ) {
                if (j + 1 <= m && p[j + 1] == '*') continue;
                if (i && p[j] != '*'&&p[j]!='?') {
                    f[i][j] = f[i - 1][j - 1] && (s[i] == p[j] || p[j] == '.');
                } else if (p[j] == '*') {
                    f[i][j] = f[i][j - 2] || i && f[i - 1][j] && (s[i] == p[j - 1] || p[j - 1] == '.');
                }else if(p[j]=='?')
                {
                   f[i][j]=f[i-1][j]||(i&&f[i-1][j-2]&&(s[i]==p[j-1]||p[j-1]=='.'));
                }
            }

        return f[n][m];
    }
};

### 关于柠檬笔试题的技术解析 根据题目描述,可以推断这是一道关于字符串匹配的算法设计问题[^1]。此类型的题目通常考察候选人对于动态规划(Dynamic Programming, DP)的理解以及实现能力。 此类问题的核心在于构建一个二维布尔型数组 `dp` 来记录子串之间的匹配状态。具体而言: - 定义 `dp[i][j]` 表示输入字符串的前 i 个字符能否被模式字符串的前 j 个字符所完全匹配。 - 初始化条件为当两个字符串均为空时,即 `dp[0][0]=True`。 - 对于其他情况,则需依据当前模式字符的不同分别处理: - 如果遇到普通字符或者 `"."` ,那么只要满足上一状态可匹配且对应位置上的字符相等即可转移; - 若碰到 `"*"` 则较为复杂些,因为其代表的是重复任意次数的情况,因此需要考虑不使用该通配符直接跳过它本身及其前置字符的情形(`dp[i][j-2]`)或者是利用这个特性继续尝试扩展已有的匹配序列长度(`if dp[i-1][j] and s[i]==p[j-1] or p[j-1]=='.'`) 以下是基于上述逻辑的一个 Python 实现版本: ```python def isMatch(s: str, p: str) -> bool: m, n = len(s), len(p) # 创建DP表并初始化 dp = [[False]*(n+1) for _ in range(m+1)] dp[0][0] = True # 处理s为空但是p可能存在的特殊情况 for j in range(2,n+1): if p[j-1]=='*' and dp[0][j-2]: dp[0][j]=True # 填充剩余部分 for i in range(1,m+1): for j in range(1,n+1): if p[j-1]=='.' or p[j-1]==s[i-1]: dp[i][j]=dp[i-1][j-1] elif p[j-1]=='*': if p[j-2]!=s[i-1] and p[j-2]!='.': dp[i][j]=dp[i][j-2] else: dp[i][j]=(dp[i][j-2])or(dp[i-1][j]) return dp[m][n] ``` 通过以上方法能够有效解决给定条件下所有的合法组合形式下的字符串全量匹配需求[^1]。 #### 注意事项 由于本解答并未借助任何外部库函数完成操作,所以符合题目要求中的限制条款规定。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值