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));
}