CSDN竞赛67期

CSDN竞赛67期

正则匹配

题目说明

给你一个字符串s和一个字符模式p,请你来原生实现一个支持 ‘.‘和’*‘的正则表达式匹配。 ‘.’ 匹配任意单个字符,’*’ 匹配零
个或多个前面的那一个元素 所谓匹配,是要涵盖整个字符串s的,而不是部分字符串

分析

这题我是手写的匹配函数,用了不少时间,而且正确率只有90%。我知道好多语言都有正则匹配的函数或库,但是我觉得不能够直接使用这些库函数,因为标准的正则表达式的元字符可能有. ^ $ * + ? { } [ ] \ | ( ),但是这些字符在本题中就只是普通字符,模式p中如果使用了这个字符肯定是会出错的。

我看到前面高分的都使用的标准函数或者库,很不理解C站的测试用例,是怎么100%通过的。而且不同语言使用的正则表达式的标准也不同,有些是用的是BRE(Basic Regular Expressions),有些是ERE(Extended Regular Expressions),结果都说是100%通过,完全无法理解。

下面的代码一个是我自己手写的支持.*的匹配,另外我也试着用linux C中的库函数来实现正则表达式,因为之前没用过库函数,就当学习一下。

真的想吐槽一下,考试报告里没有给出使用的测试用例,赛后真的很难分析代码的问题。

事后参考标准库函数,我觉得我的代码的问题是对于*号匹配0次的理解不对,匹配0次应该是前面的字符不匹配。假设*号在j位置,匹配j-2的是i,j+1(*后面的字符)要和i+1匹配。

代码

1.使用库函数,注意这个库函数只在Linux上有,不通用。而且应该不适用本题

#include <stdio.h>
#include <sys/types.h>
#include <regex.h>
#include <stdbool.h>
#include <string.h>

bool reg_match(char *string, char *reg_str)
{
    regex_t preg;
    int ret;
    int cflag = 0;
    char ebuff[128];
    regmatch_t matchptr[1];

    ret = regcomp(&preg, reg_str, cflag);
    if (ret)
    {
        goto err;
    }

    ret = regexec(&preg, string, 1, matchptr, 0);
    if (ret)
        goto err;
    
    regfree(&preg);
    if (matchptr[0].rm_so == 0 && matchptr[0].rm_eo == strlen(string))
        return true;
    else
        return false;
err:
    regerror(ret, &preg, ebuff, 128);
    printf("regcomp error:%s\n",ebuff);
    regfree(&preg);
    return false;
}
 
int main(int argc,char** argv)
{
    char str1[10000] = {0};
    char str2[10000] = {0};
    char *x1 = gets(str1);
    char *x2 = gets(str2);
    if (reg_match(str1,str2))
        printf("true");
     else
        printf("false");
    return 0;
}

2.手写代码

#include <stdio.h>
#include <string.h>
#include <stdbool.h>
bool match(char *str1, char *str2) 
{
    int len = strlen(str1);
    int j = 0;
    char curr;
    int count = 0;
    for (int i = 0; i < len || j < strlen(str2); i++) 
    {
        curr = str2[j];
        if (curr == '.')   // 点匹配任意字符
        {
            j++;
            continue;
        } 
        else if (curr == '*')
        {
            char before = str2[j-1];
            {
                if (before == '.' || before == str1[i])
                {
                    count++;  // 匹配前一个字符多次
                } else 
                {
                    j++;
                    if (count == 0)
                        i--;
                    count = 0;
                }
            }
        } 
        else   // 普通字符
        {
            if (curr != str1[i])
            {
                if (str2[j+1] == '*')   // 可以匹配0次
                {
                    j++;
                    i--;
                }
                else
                    return false;
            }
            j++;
        }
    }

    return true;
}
int main() 
{
    char str1[10000] = {0};
    char str2[10000] = {0};
    gets(str1);
    gets(str2);
    if (match(str1,str2))
        printf("true"); 
    else
        printf("false");
}

生命进化书

题目说明

小A有一本生命进化书,以一个树形结构记载了所有生物的演化过程。 为了探索和记录其中的演化规律,小A提出了一种方法,可以以字符串的形式将其复刻下来,规则如下: 初始只有一个根节点,表示演化的起点,依次记录 01 字符串中的字符, 如果记录 0,则在当前节点下添加一个子节点,并将指针指向新添加的子节点; 如果记录 1,则将指针回退到当前节点的父节点处。 现在需要应用上述的记录方法,复刻下它的演化过程。请返回能够复刻演化过程的字符串中, 字典序最小的 01 字符串; 注意:节点指针最终可以停在任何节点上,不一定要回到根节点。

分析

这个题目在规定时间每把代码写完,交卷后又写的,试了一些例子,应该是没问题的

解题思路:
最开始的想法是递归,遍历某个节点为根节点的树,就是分别遍历所有的子树,但是因为要求按最小的字典序来遍历,而且还要涉及是否回归根节点的问题。如果用递归,那么不回根节点和回根节点两种形式都要递归,必定超时,而且两种形式都递归代码也不好写
其实很多问题能用递归的方式解决,就能使用动态规划,因为数组是递增的,数组第i个接待你的子树一定在i的右边,所以可以从右向左计算每个节点不回根节点和回根节点两种形式的字符序列,并保存

代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAX_CHILD_NUM   (10)   // 子节点的最大数量

int compare(const void *a, const void *b)
{
    return strcmp(*(char **)b,*(char **)a);
}

char* get_min_str(int a[], int n) {
    char *str[2][n];  // 0不返回根节点的遍历字符序列,1返回根节点
    int i,j;
    for (i = n-1; i >=0; i--)  // 遍历数组
    {
        int node = i;
        char *child[2][MAX_CHILD_NUM];
        int child_number = 0;
        int str_len_count = 0;
        for (j = i+1; j < n; j++)  // 查找子节点
        {
            if (a[j] == node)
            {
                child[0][child_number] = (char *)malloc(strlen(str[0][j])+1+1);
                memset(child[0][child_number],0,strlen(str[0][j])+1+1);
                strcat(child[0][child_number], "0");  // 进入子节点
                strcat(child[0][child_number], str[0][j]); // 遍历子树


                child[1][child_number] = (char *)malloc(strlen(str[1][j])+2+1);
                memset(child[1][child_number],0,strlen(str[1][j])+2+1);
                strcat(child[1][child_number], "0"); // 进入子节点
                strcat(child[1][child_number], str[1][j]);// 遍历子树并回到子节点
                strcat(child[1][child_number], "1"); // 回到当前节点
                str_len_count += strlen(child[1][child_number]);
                child_number++;
            }
        }
        if (child_number == 0) // 没有子节点
        {
            str[0][i] = (char *)malloc(1);
            str[1][i] = (char *)malloc(1);
            strcpy(str[0][i],"");
            strcpy(str[1][i],"");
        }
        else
        {
            // 题目要求字典序最小,对字符串排序,由小到大,这里为了简单用了冒泡排序
            for (int k = 0; k < child_number; k++)
            {
                for (int m = 0; m < child_number - k -1; m++)
                {
                    if (strcmp(child[1][m], child[1][m+1]) > 0)
                    {
                        char *ptmp = child[1][m];
                        child[1][m] = child[1][m+1];
                        child[1][m+1] = ptmp;

                        ptmp = child[0][m];
                        child[0][m] = child[0][m+1];
                        child[0][m+1] = ptmp;
                    }
                }
            }
            // 计算节点i的字典序遍历
            str[0][i] = (char *)malloc(str_len_count + 1);
            memset(str[0][i],0,str_len_count + 1);
            str[1][i] = (char *)malloc(str_len_count + 1);
            memset(str[1][i],0,str_len_count + 1);
            for (int k = 0; k < child_number-1; k++)  // 前child_number-1个子节点需要回到根节点
            {
                strcat(str[0][i],child[1][k]);

                strcat(str[1][i],child[1][k]);

                free(child[1][k]);
                free(child[0][k]);
            }
            strcat(str[0][i],child[0][child_number-1]); // 不回根节点
            strcat(str[1][i],child[1][child_number-1]); // 回根节点

            free(child[1][child_number-1]);
            free(child[0][child_number-1]);

            // printf("%s\n",str[0][i]);
        }
    }
    return str[0][0];
}
int main() {
    int parents[10000];
    int count = 0;
    char c;
    scanf("[%d",&parents[count++]);
    while((c = getchar()) != ']') {
        scanf("%d",&parents[count++]);
    }
    printf("%s",get_min_str(parents,count));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
软件测试竞赛题目csdn是一个面向软件测试领域的竞赛平台,旨在提升软件测试技能和知识能力。这个平台上的竞赛题目主要涵盖了软件测试的各个方面,包括测试策略、测试用例设计、缺陷管理和自动化测试等内容。参与者可以通过参加这些竞赛,与其他测试人员进行技术交流和切磋,提高自己的测试水平,同时也可以通过竞赛来进行实践,锻炼自己的软件测试技能。 在软件测试竞赛题目csdn上,参与者可以选择自己感兴趣的竞赛题目进行参与。这些题目根据难易程度和内容类型进行分类,参与者可以根据自己的实际情况选择适合自己的题目进行参与。在参与过程中,参赛者需要根据题目要求制定测试策略和设计相应的测试用例,同时还需要进行测试执行和缺陷管理等工作。 通过参与软件测试竞赛题目csdn,参赛者可以不仅可以提升自己的软件测试技能,还可以与其他测试人员进行经验交流,了解业界最新的测试方法和技术。此外,优秀的参赛者还有机会被一些知名软件测试公司或者人才中介公司发现,从而获得更好的职业发展机会。 总之,软件测试竞赛题目csdn为软件测试人员提供了一个学习和交流的平台,能够有效地提升软件测试技能和知识水平。通过参与这些竞赛,参赛者可以提升自己的测试能力,同时也可以通过竞赛获得更好的职业发展机会。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值