1.7 LeetCode总结(线性表)字符串类

编程总结

每每刷完一道题后,其思想和精妙之处没有地方记录,本篇博客用以记录刷题过程中的遇到的算法和技巧

在这里插入图片描述

412. Fizz BuzzI

写一个程序,输出从 1 到 n 数字的字符串表示。

  1. 如果 n 是3的倍数,输出“Fizz”;
  2. 如果 n 是5的倍数,输出“Buzz”;
    3.如果 n 同时是3和5的倍数,输出 “FizzBuzz”。

本题重点测试 sprintf函数和指向指针的指针的空间分配处理

char **fizzBuzz(int n, int* returnSize) {
	int len = 0;
	int in = n;
	int i;
	char **ret;

	ret = (char**)malloc(sizeof(char*) * n); //分配n个指针,每个指针分别指向一个地址
	*returnSize = n;

	do {
		len++;
		in /= 10;
	} while (in);

	for (i = 1; i <= n; i++) {  // 0 不记录,从1开始记录
		if (i % 3 == 0 && i % 5 == 0) {
			ret[i - 1] = (char *)malloc(strlen("fizzfuzz") + 1);
			sprintf(ret[i - 1], "%s", "FizzBuzz");
		}
		else if (i % 5 == 0) {
			ret[i - 1] = (char *)malloc(strlen("Buzz") + 1);
			sprintf(ret[i - 1], "%s", "Buzz");
		}
		else if (i % 3 == 0) {
			ret[i - 1] = (char *)malloc(strlen("Fizz") + 1);
			sprintf(ret[i - 1], "%s", "Fizz");
		}
		else {
			ret[i - 1] = (char *)malloc(len + 1);
			sprintf(ret[i - 1], "%d", i);
		}
	}

	return ret;
}

14. 最长公共前缀

在这里插入图片描述
关键手法:横向扫描法

char *longestCommonPrefix(char **strs, int strsSize)
{
	int i = 0, j = 0;
	char *ret = (char *)malloc(sizeof(char) * (strlen(strs[0])+1)); // 细节啊,这里必须要分配多一个'\0'的空间才行.
	int cnt = 0;
	int flag = 0;

	if (strsSize == 1) {
		return strs[0];
	}
	// 横向扫描法,保持列不动,看字符串数组里多少个列为同字符.
	// 遇到不同的字符即退出
	for (j = 0; j < strlen(strs[0]); j++) {
		for (i = 0; i < (strsSize - 1); i++) {
			if (strs[i][j] != strs[i + 1][j]) {
				flag = 1;
				break;  // 一个break只会跳出一层
			}
		}
		if (flag == 0) {
			ret[cnt++] = strs[i][j];
		} else {
			break;
		}
	}
	ret[cnt] = '\0';

	return ret;
}

3. 无重复字符的最长子串

在这里插入图片描述
关键手法:滑动窗口法/双指针法

// 双指针法 -- 滑动窗口法
int lengthOfLongestSubstring(char * s) {
	int res = 0;
	int len = strlen(s);
	int hash[256] = { 0 };

	// 定义滑动窗口为 s[l...r] 
	int l = 0, r = -1;
	while (l < len) {
		// hash 中不存在该字符,右边界右移,并将该字符出现的次数记录在 hash 中 
		if (r < len - 1 && hash[s[r + 1]] == 0) {
			hash[s[++r]]++;
		}
		else {
			// 右边界无法拓展,左边界右移,刨除重复元素,并将此时左边界对应的字符出现的次数在 hash 的记录中减一 
			hash[s[l++]]--;
		}
		// 当前子串的长度和已找到的最长子串的长度取最大值 */
		res = fmax(res, r - l + 1);
	}

	return res;
}

151. 翻转字符串里的单词

在这里插入图片描述

#define MAXSIZE 10001
#define STRSIZE 256

typedef struct Node {
	char str[STRSIZE];
} Node_t;

char *reverseWords(char *s)
{
	char  *buf;
	const char p[2] = " ";
	char  *token;
	int   len = strlen(s);
	const char *plim = " ";
	const char *blank = "\0";
	int   i;
	Node_t *node = (Node_t *)malloc(sizeof(Node_t) * MAXSIZE);
	int   cnt = 0;
	char  res[MAXSIZE] = {0};

	memset(res, 0, MAXSIZE * sizeof(char));

	// 去除字符串的空格
	token = strtok_s(s, p, &buf);
	while (token != NULL) {
		memcpy(node[cnt].str, token, strlen(token));
		node[cnt].str[strlen(token)] = '\0';
		cnt++;
		token = strtok_s(NULL, p, &buf);
	}

	// 翻转字符串
	i = cnt - 1;
	while (i >= 0) {
		strcat_s(res, node[i].str);
		if (i != 0) {
			strcat_s(res, plim);
		}
		else {
			strcat_s(res, blank);
		}
		i--;
	}
	free(node);
	memset(s, 0 ,  len);
	memcpy(s, res, strlen(res));  // 其实这里返回 res/s都可以,但如果是返回res,不能是局部变量

	return s;
}

165. 比较版本号

在这里插入图片描述
手法1: strtok 的分割,按 “.” 分割开后,利用atoi函数都转成整数,再比较一下即可,思路比较清晰.

#define MAX_SIZE    501

void get_version_num(char *version, int *res, int *num)
{
    char strs[MAX_SIZE] = {0};
    char *token = NULL;
    const char *plim = ".";    // 分隔符
    int n = 0;

    // 调用strtok函数时,形参要给字符串数组,所以多做了一次strncpy拷贝.
    strncpy(strs, version, strlen(version) + 1);
    token = strtok(strs, plim); // char *strtok(char *restrict str,const char *restrict delim);
    while (token != NULL) {
        res[n] = atoi(token);
        n = n + 1;
        token = strtok(NULL, ".");
    }
    *num = n;
}

int compareVersion(char *version1, char *version2)
{
    int num1 = 0;
    int num2 = 0;
    int maxNum;
    int res1[MAX_SIZE] = {0};
    int res2[MAX_SIZE] = {0};
    int i;

    if (version1 == NULL || version2 == NULL) {
        return -1;
    }

    get_version_num(version1, res1, &num1);
    get_version_num(version2, res2, &num2);
    // 手法:取最大的Num作为比较的次数,空缺的话,默认值为0.
    maxNum = num1 > num2 ? num1 : num2;

    for (i = 0; i < maxNum; i++) {
        if (res1[i] > res2[i]) {
            return 1;
        } else if (res1[i] < res2[i]) {
            return -1;
        }
    }

    return 0;
}

手法2:num1 = num1 * 10 + (version1[i++] - ‘0’); 利用每一次到 ‘.’ 之前都记录下整数,每次直接比较整数,更为高效。

int compareVersion(char * version1, char * version2){
    int len1 = strlen(version1);
    int len2 = strlen(version2);
    int i = 0;
    int j = 0;
    while (i < len1 || j < len2) {
        int num1 = 0;
        int num2 = 0;
        while (i < len1 && version1[i] != '.') {
            num1 = num1 * 10 + (version1[i++] - '0');
        }
        while (j < len2 && version2[j] != '.') {
            num2 = num2 * 10 + (version2[j++] - '0');
        }
        if (num1 < num2) {
            return -1;
        } else if (num1 > num2) {
            return 1;
        }
        i++;
        j++;
    }
    return 0;
}

5789. 你完成的完整对局数

在这里插入图片描述
在这里插入图片描述
借鉴题解思路后:我们可以将 t_1 ​转化为 t_1 或之前时刻最后一场完整对局的结束时间 t_1′,或将 t_0转化为t_0或之后时刻第一场完整对局的开始时间t_0′​即可,这样计算总的对局次数没有变.
需要处理 finish_time 在start_time之前的情况(包夜了),此时如果没有转成分钟来做,要注意这种情况的处理:
start_time – 00:01
finish_time – 00:00
所以还是统一转成分钟判断处理吧.

#define MAXSIZE  6  
int numberOfRounds(char *startTime, char *finishTime)
{
	char strs1[MAXSIZE] = { 0 };
	char strs2[MAXSIZE] = { 0 };
	const char *plim = ":";     // 存放分隔符
	char *token;                // 返回值,切割完的字符串
	int i = 0;
	int start[2]  = { 0 };
	int finish[2] = { 0 };
	int res = 0;
	int times0 = 0;
	int times1 = 0;

	// 1. 将时间字符串切割
	strcpy(strs1, startTime);
	strcpy(strs2, finishTime);
	token = strtok(strs1, plim);
	while (token != NULL) {
		start[i++] = atoi(token);
		token = strtok(NULL, plim);
	}
	token = strtok(strs2, plim);
	i = 0;
	while (token != NULL) {
		finish[i++] = atoi(token);
		token = strtok(NULL, plim);
	}

	times0 = 60 * (start[0])  + start[1];
	times1 = 60 * (finish[0]) + finish[1];

	// 2. 计算对局次数
	if (times1 < times0) {
		times1 += 24*60;        // 包夜了,增加24小时
	}
	times1 = times1 / 15 * 15;  // 按15min对齐
	res = ((times1 - times0)/15);

	return res;
}

401. 二进制手表

在这里插入图片描述
在这里插入图片描述
手法1:sprintf(tmp, “%d:%02d”,h, m); 来处理,为啥是%02d
手法2:__builtin_popcount()内嵌函数处理1在整数中的数量

char **readBinaryWatch(int turnedOn, int *returnSize) 
{
	int h;
	int m;
	char **ans = (char **)malloc(sizeof(char *) * 12 * 60);
	*returnSize = 0;
	for (h = 0; h < 12; ++h) {
		for (m = 0; m < 60; ++m) {
			if ((__builtin_popcount(h) + __builtin_popcount(m)) == turnedOn) {
				printf("%d\n", m);
				char *tmp = (char *)malloc(sizeof(char) * 6);
				sprintf(tmp, "%d:%02d",h, m);
				ans[(*returnSize)++] = tmp;
			}
		}
	}

	return ans;
}

179. 最大数

在这里插入图片描述

int camp(const void *v1, const void *v2) 
{
	static char buf1[512], buf2[512];
	int len;
	//返回写入buffer 的 字符数,出错返回-1
	len = sprintf(buf1, "%d%d", *(int *)v1, *(int *)v2);
	len = sprintf(buf2, "%d%d", *(int *)v2, *(int *)v1);

	// 对数组进行降序排列
	for (int i = 0; i < len; ++i) {
		if (buf1[i] > buf2[i]) {
			return -1;
		}
		if (buf1[i] < buf2[i]) {
			return 1;
		}
	}

	return 0;
}

char *largestNumber(int *nums, int numsSize) 
{
	static char str[1024];
	int i = 0, j = 0;
	qsort(nums, numsSize, sizeof(int), camp);
	str[0] = '0';
	str[1] = '\0';
	while (i < numsSize && nums[i++] == 0); // 有个用例是 [0,0] -》“0” 而不是“00”, 去掉前导零
	for (i--; i < numsSize; ++i) {
		j += sprintf(str + j, "%d", nums[i]);
	}

	return str;
}

面试题 01.03. URL化

在这里插入图片描述

//双指针法,一个从旧数组末尾开始遍历,一个从新数组开始遍历添加
char* replaceSpaces(char* S, int length)
{
	int SpaceNums = 0;
	for (int i = 0; i < length; i++)
	{
		if (S[i] == ' ')
			SpaceNums++;//判断有几个空格
	}
	int NewLength = length + SpaceNums * 2;//新长度=原长度+空格数*2
	S[NewLength] = '\0';//避免过长导致最后一位字符为原题所给的空格;
	int newlength = NewLength - 1;

	for (int i = length - 1; i >= 0; i--)//不开辟新数组的情况下,从后往前遍历,避免覆盖
	{
		if (S[i] == ' ') {
			S[newlength--] = '0';
			S[newlength--] = '2';
			S[newlength--] = '%';
		}
		else {
			S[newlength--] = S[i];
		}
	}

	return S;
}

1071. 字符串的最大公因子

在这里插入图片描述
试试巧妙的递归也是一个思路

// 1071. 字符串的最大公因子
// 如果两个字符串之间存在最大公因子: 
//(1) 短串一定是和在长串的前strlen(短串)字符组成的子串相同; 
//(2) 长串不断减去短串,得到的两个字符串(原有短串和差串)同样满足(1)中关系; 
//(3) 当等到的两个串相同时,即为最大公因子
char *gcdOfStrings(char *str1, char *str2)
{
	char *lger = strlen(str1) > strlen(str2) ? str1 : str2;
	char *sher = strlen(str1) > strlen(str2) ? str2 : str1;

	//(1) 短串一定是和在长串的前strlen(短串)字符组成的子串相同; 
	if (strcmp(lger, sher) == 0) {
		return sher;
	}

	// 字符串不存在,返回 ""
	if (strncmp(lger, sher, strlen(sher)) != 0) {
		return "";
	}

	//(2) 长串不断减去短串,得到的两个字符串(原有短串和差串)同样满足(1)中关系; 
	return gcdOfStrings(lger + strlen(sher), sher);
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值