LeetCode刷题笔记(二)——初级算法——字符串


终于做完了初级算法的数组部分,算是阶段性成果吧,只是做这个题吧就挺费时间的,总想自己想出方法来,但是遇到个稍微复杂点的题就耗费半天甚至一天的时间去想去试,看了答案吧,又觉得别人的解法真好,自己要学一学,于是有用了好长的时间。。。希望以后效率能够更高吧

❤ 2021.9.6 ❤
今天的题目是

反转字符串

在这里插入图片描述
我的思路:这道题做出答案确实挺简单的,就是数组的交换,甚至在之前的题里这道题只是答案的一部分而已。

void reverseString(char* s, int sSize) 
{
	char temp;
	for (int i = 0; i < sSize / 2; i++)
	{
		temp = s[i];
		s[i] = s[sSize - 1 - i];
		s[sSize - 1 - i] = temp;
	}
	return 0;
}

在这里插入图片描述
但是呢,越是简单的题越是考验基础,在答案里有人给出了这么多种交换方法
在这里插入图片描述
非常有借鉴意义!

❤ 2021.9.8 ❤
今天的题目是

整数反转

在这里插入图片描述
在这里插入图片描述
我的思路:
看到这个题吧,第一感觉不难,把数字处理成数组,然后反转再处理成整型,再输出。可是考虑到这个有符号整型的范围怎么去判断呢。。。
题目里不让存储64位的数据,那么也就是说不能先把结果存到64位长整型里面再做比较,那么只能在数组反转后去判断了。

int reverse(int x) {
	int fushu = 0;
	int weishu = 1;
	unsigned int unsignedNumber = 0;
	if (x < 0)
	{
		fushu = 1;
		x = -x;
	}
	while (x/(int)pow(10,weishu) != 0)
		weishu++;
	for (int i = 0; i < weishu; i++)
	{
		unsignedNumber += x % 10 * pow(10, weishu - 1 - i);
		x /= 10;
	}
	if ((fushu == 0 && unsignedNumber > pow(2, 31) - 1) || (fushu == 1 && unsignedNumber > pow(2, 31)))
		return 0;
	else
		return pow(-1, fushu) * (int)unsignedNumber;
}

首先判断是否为负,如果是负数则取反并记录负数标记;然后计算数字的位数并储存;然后将原数字一位一位取出并放置在对应反转后的位置上,刚开始我用了一个临时数组,后来发现不用数组也可以,直接新建一个无符号的整型数,并叠加给他;利用无符号数存储反转后的数字再和有符号数字的极限相比较,如果超出则返回0,没有超出则返回对应数值。
但是。。。
在这里插入图片描述
但诡异的是,我在vs里测试相同的数字,输出是正确的
在这里插入图片描述
这就。。。。看答案
答案给出的思路和我的差不多,但是比我的简洁很多,并没有去判断正负号以及转换无符号数,而是直接用while循环在每个周期都把上次的结果x10并加上本次的余数,判断超范围的方法则是利用逆运算法,在每个周期中,若逆运算后的结果和初始不同则返回0。

int reverse(int x) {
	int returnNumber = 0;
	while (x != 0)
	{
		int t = x % 10;
		int newNumber = returnNumber * 10 + t;
		x /= 10;
		if ((newNumber - t) / 10 != returnNumber)
			return 0;
		returnNumber = newNumber;
	}
	return returnNumber;
}

但是。。。出问题了
在这里插入图片描述
大概意思就是溢出了。。。
我判断的就是溢没溢出,你告诉我不能溢出,你在逗我么???
然后我改了改

int reverse(int x) {
	long returnNumber = 0;
	while (x != 0)
	{
		int t = x % 10;
		int newNumber = returnNumber * 10 + t;
		x /= 10;
		if ((newNumber - t) / 10 != returnNumber)
			return 0;
		returnNumber = newNumber;
	}
	return (int)returnNumber;
}

就是把返回值的int类型改成了long型,然后return的时候再改回来
在这里插入图片描述
这TM也可以!虽然其他人提交的答案里也有中间变量用long型的,但是题目里明确不可以用啊。。。
所以得出结论:这道题有问题!
但是看了官方答案,我觉得我还是太年轻了
在这里插入图片描述
大概意思就是,其他人的答案基本都是溢出之后去判断是否溢出,所以会报错,但是官方答案是通过推导,判断x10后如果会溢出的前提条件,非常有借鉴意义!
官方答案

❤ 2021.9.9 ❤
今天的题目是

字符串中的第一个唯一字符

在这里插入图片描述
在这里插入图片描述
我的思路:
双指针嘛!

int firstUniqChar1(char* s) {
	int n = strlen(s);
	int i = 0, j = 1;
	while (i < n && j < n)
	{
		if (s[i] != s[j])
		{
			j++;
			if (j == n)
				return i;
		}
		else
		{
			i++;
			j = i + 1;
		}
	}
	return -1;
}

然后不对
在这里插入图片描述
分析一下,我的双指针没有检索前面的元素,所以会这样,另外我在写代码的时候把if里面的==写成了=,于是出现了奇怪的问题。。。。
修改一下

int firstUniqChar(char* s) {
	int n = strlen(s);
	int i = 0, j = 0;
	while (i < n && j < n)
	{
		if (i != j)
		{
			if (s[i] != s[j])
			{
				j++;
				if (j == n)
					return i;
			}
			else
			{
				i++;
				j = 0;
			}
		}
		else
			j++;
	}
	return -1;
}

在这里插入图片描述
我去。。。这也可以。。。。
我再改

int firstUniqChar(char* s) {
	int n = strlen(s);
	int i = 0, j = 0;
	if (n == 1)
		return 0;
	while (i < n && j < n)
	{
		if (i != j)
		{
			if (s[i] != s[j])
			{
				j++;
				if (j == n )
					return i;
			}
			else
			{
				i++;
				j = 0;
			}
		}
		else
			j++;
	}
	return -1;
}

可是。。。
在这里插入图片描述
我去。。。没想到啊没想到
再改

int firstUniqChar(char* s) {
	int n = strlen(s);
	int i = 0, j = 0;
	if (n == 1)
		return 0;
	while (i < n && j < n)
	{
		if (i != j)
		{
			if (s[i] != s[j])
			{
				j++;
				if (j == n || i == n - 1)
					return i;
			}
			else
			{
				i++;
				j = 0;
			}
		}
		else
			j++;
	}
	return -1;
}

哈?按下葫芦浮起瓢?
在这里插入图片描述
再改。。。我就不信了。。。

int firstUniqChar(char* s) {
	int n = strlen(s);
	int i = 0, j = 0;
	if (n == 1)
		return 0;
	while (i < n && j < n)
	{
		if (i != j)
		{
			if (s[i] != s[j])
			{
				j++;
				if (j == n )
					return i;
			}
			else
			{
				i++;
				j = 0;
			}
		}
		else 
		{
			j++;
			if (i == n - 1)
				return i;
		}
	}
	return -1;
}

终于。。。
在这里插入图片描述

然后我看了答案,好像没有人用我这种毫无美感的暴力搜索方法。。。
针对这道题的特点,因为字符串中只有小写字母,所以种类只有最多26个,于是可以以字母作为线索来处理问题。
思路1:针对字符串中的某个元素,同时从正向和方向去遍历,如果得到的是同一个索引,则返回,不是则继续。
思路2:哈希表储存频率,第一次遍历记录字母出现的频率,第二次找到第一个频率为1的元素,并返回索引。
思路3:哈希表存储索引,就是在第一遍检索的时候,如果被检索的字母已经被记录过,那就记为-1,第二次检索时返回第一个不是-1的索引。
我试了试思路2

int firstUniqChar(char* s) {
	int frequence[26] = { 0 };
	int n = strlen(s);
	for (int i = 0; i < n; i++)
	{
		frequence[s[i] - 'a']++;
	}
	for (int i = 0; i < n; i++)
	{
		if (frequence[s[i] - 'a'] == 1)
			return i;
	}
	return -1;
}

在这里插入图片描述
学到了学到了

❤ 2021.9.10 ❤
今天的题目是

有效的字母异位词

在这里插入图片描述
在这里插入图片描述
我的思路:
简单!不就是跟昨天那个差不多么!
先建立一个键为26个小写字母的哈希表,然后统计字母出现的频数,然后比较两组字符串的频数,相同则为true。

bool isAnagram(char* s, char* t) {
	int temps[26] = { 0 };
	int tempt[26] = { 0 };
	int ns = strlen(s);
	int nt = strlen(t);
	for (int i = 0; i < ns; i++)
		temps[s[i] - 'a']++;
	for (int i = 0; i < nt; i++)
		tempt[t[i] - 'a']++;
	for (int i = 0; i < 26; i++)
	{
		if (temps[i] != tempt[i])
			return false;
	}
	return true;
}

在这里插入图片描述
不过看了答案之后,感觉思维被昨天的题目局限了,还有更简单的方法,就是先排序,再逐位比较。

int cmpstr(const void* _a, const void* _b) {
	int a = *(char*)_a, b = *(char*)_b;
	return a - b;
}

bool isAnagram(char* s, char* t) {
	int ns = strlen(s);
	int nt = strlen(t);
	if (ns != nt)
		return false;
	qsort(s, ns, sizeof(s[0]), cmpstr);
	qsort(t, nt, sizeof(t[0]), cmpstr);
	for (int i = 0; i < ns; i++)
	{
		if (s[i] != t[i])
			return false;
	}
	return true;
}

在这里插入图片描述
执行没有问题,虽然慢了点,但是调试的时候出了点问题
在这里插入图片描述
查了一下说是win10更新之后的不兼容问题
在这里插入图片描述
VS2019未加载ucrtbased.pdb错误怎么解决?

但是我安装了里面提到的sdk还是不行,然后我在vs里选了那两个符号服务器,选择加载,还是不行,但是在leetcode里面运行是通过的,所以先放放吧。
顺便一提,官方答案里面的哈希表法,用了一个哈希表,第一个字符串用++,第二个用–,最后检索是否为零,这个方法很巧妙~
官方答案的排序法里面用了strcmp()这个函数
C 库函数 - strcmp()
学到了学到了

我又用c++试了排序的方法

class Solution {
public:
    bool isAnagram(string s, string t) {
        if (s.size() != t.size())
            return false;
        sort(s.begin(), s.end());
        sort(t.begin(), t.end());
        return s == t;
    }
};

虽然这个代码是我抄来的,思想和c的差不多,但是实现起来就TM很简洁!
首先里面用了string类型,不得不说真的是良心类型,比char*强多了。
另外里面的sort()函数需要include一个#include<algorithm>
然后调试的过程中,虽然也出现了字符的问题,但是他自己下载了字符库,并没有报错,赞
在这里插入图片描述
这个时候360跳出来了
在这里插入图片描述
我:!!!

❤ 2021.9.10 ❤
今天的题目是

验证回文串

在这里插入图片描述
在这里插入图片描述
我的思路:
首先把字符串处理成全由小写字母构成,然后一对一对的判断开头结尾是不是相同。

bool isPalindrome(char* s) {
	int n = strlen(s);
	int i = 0, j = 0;
	char* temp = (char*)malloc(n);
	if (n == 0)
		return true;
	while (j<n)
	{
		if ((s[j] - 'a' >= 0 && s[j] - 'a' <= 26) || (s[j] - '0' >= 0 && s[j] - '0' <= 9))
		{
			temp[i] = s[j];
			i++;
			j++;
		}
		else if (s[j] - 'A' >= 0 && s[j] - 'A' <= 26)
		{
			temp[i] = s[j] + 32;
			i++;
			j++;
		}
		else
			j++;
	}
	for (int k = 0; k < i / 2; k++)
	{
		if (temp[k] != temp[i - 1 - k])
			return false;
	}
	return true;
}

我本来打算直接在字符串s里面用双指针的方法来修改,但是调试的时候报错了
在这里插入图片描述
我百思不得其解。。。于是百度了一下,才知道原来用char*的形式定义的字符串是不能直接对里面的元素进行修改的!
c语言中使用数组和指针定义字符串的区别
所以我用了动态分配内存的方法重新建了个数组。
另外这里的i在最后一个有效的元素被添加进来后是加了1的,所以记得在后面的时候-1;
但是。。。
在这里插入图片描述
喵喵喵?
我中间加了个printf,发现。。。
在这里插入图片描述
原来里面加入了不明的{,这是哪来的呢?
在这里插入图片描述
心几次哇一次某黑他次!原来是小写字母的后一个,然鹅为什么会把他加进去呢?原来我用字符串元素-'a’得到字母的范围应该是0-25,而不是26。。。我去

bool isPalindrome(char* s) {
	int n = strlen(s);
	int i = 0, j = 0;
	char* temp = (char*)malloc(n);
	if (n == 0)
		return true;
	while (j<n)
	{
		if ((s[j] - 'a' >= 0 && s[j] - 'a' < 26) || (s[j] - '0' >= 0 && s[j] - '0' <= 9))
		{
			temp[i] = s[j];
			i++;
			j++;
		}
		else if (s[j] - 'A' >= 0 && s[j] - 'A' < 26)
		{
			temp[i] = s[j] + 32;
			i++;
			j++;
		}
		else
			j++;
	}
	for (int k = 0; k < i / 2; k++)
	{
		if (temp[k] != temp[i - 1 - k])
			return false;
	}
	return true;
}

在这里插入图片描述
OK!
另外要注意,数字在这里是不适用的!就是<=9!

下面试一试c++
这里换一种算法,直接在字符串上面做判断,用双指针对撞的方法。

class Solution {
public:
    bool isPalindrome(string s) {
        int n = s.size();
        int left = 0, right = n - 1;
        while (left < right)
        {
            while (left < right && !isalnum(s[left]))
                left++;
            while (left < right && !isalnum(s[right]))
                right--;
            if (left < right)
            {
                if (tolower(s[left]) != tolower(s[right]))
                    return false;
            }
            left++;
            right--;
        }
        return true;
    }
};

抄的代码我就不放到leetcode里面跑了,c++的代码里面用到了几个c++的库函数,让整个过程非常顺滑。。。
isalnum()
是判断字符是否为数字和字母的函数,相同的还有以下
C++ isalpha、isalnum、islower、isupper用法
tolower()是将大写字母转换为小写字母的函数
C/C++库函数(tolower/toupper)实现字母的大小写转换
学到了学到了

❤ 2021.9.11 ❤
今天的题目是:

字符串转换整数 (atoi)

在这里插入图片描述
首先吐槽一下,本来今天的题我已经做完笔记了,但是没保存的情况下关闭了浏览器,于是。。。
垃圾CSDN!
我的思路:
先判断开头的空格,直到不是空格为止,然后判断是否是除了正负号和数字的其他字符,如果是则false,如果不是则判断正负,然后把数字字符存为整型数,最后输出

int myAtoi(char* s) {
	int n = strlen(s);
	int fuhao = 0;
	int temp = 0;
	int i = 0;
	if (n == 0)
		return 0;
	while (s[i] == ' ')
		i++;
	if (s[i] != '+' && s[i] != '-' && !(s[i] - '0' >= 0 && s[i] - '0' <= 9))
		return  0;
	if (s[i] == '+' || s[i] == '-')
	{
		if (s[i + 1] - '0' >= 0 && s[i + 1] - '0' <= 9)
		{
			if (s[i] == '-')
			{
				fuhao = 1;
				i++;
			}
			else
				i++;
		}
		else
			return 0;
	}
	while (i < n && s[i] - '0' >= 0 && s[i] - '0' <= 9)
	{
		if (fuhao == 1)
		{
			temp = -abs(temp);
			if (temp < (INT_MIN + (s[i] - '0')) / 10)
				return INT_MIN;
			temp = temp * 10 - (s[i] - '0');
		}
		else
		{
			if (temp > (INT_MAX - (s[i] - '0')) / 10)
				return INT_MAX;
			temp = temp * 10 + (s[i] - '0');
		}
		i++;
	}
	return temp;
}

在这里插入图片描述
在这里我陷入了固有思维,觉得像之前那个题一样,存在32位溢出判断就要在32位的存储类型中来解决,于是卡在32位整型的正负极限值那里好久,然而实际上题目里并没有要求,大神们给的答案也基本都用了长整型或者double型。不过我还是参考之前那题把结果整出来了,首先用abs()函数实现每次迭代的符号都是确定的,然后用极限值减去本次叠加值再除10的方法判断本次迭代后是否会溢出。
然后这次得到了一个经验就是不要在没有#include<math.h>的情况下使用pow()。。。
最后感慨一句
在这里插入图片描述
至于官方答案中的有限状态机方法我还不是很了解,以后碰到了再说吧。

❤ 2021.9.13 ❤
今天的题目是

实现 strStr()

在这里插入图片描述
在这里插入图片描述
我的思路:
首先在haystack里面找needle[0],找到之后从这位开始遍历看看是不是包含needle,如果包含则返回对应位置,不包含再继续找。

int strStr(char* haystack, char* needle) {
	int n1 = strlen(haystack);
	int n2 = strlen(needle);
	int i = 0;
	if (n2 == 0)
		return 0;
	while (i < n1)
	{
		if (haystack[i] != needle[0])
		{
			i++;
		}
		else
		{
			for (int k = 0; k < n2; k++)
			{
				if (haystack[i + k] != needle[k])
				{
					break;
				}
				return i;
			}
			i++;
		}
	}
	return -1;
}

结果
在这里插入图片描述
好吧题中并没有说needle一定比haystack小

int strStr(char * haystack, char * needle){
	int n1 = strlen(haystack);
	int n2 = strlen(needle);
	int i = 0;
	if (n2 == 0)
		return 0;
	if (n1 < n2)
		return -1;
	while (i < n1)
	{
		if (haystack[i] != needle[0])
		{
			i++;
		}
		else
		{
			for (int k = 0; k < n2; k++)
			{
				if (i + k == n1)
					return -1;
				if (haystack[i + k] != needle[k])
				{
					break;
				}
				return i;
			}
			i++;
		}
	}
	return -1;
}

在这里插入图片描述
加上了关于字符串长度的判断和遍历needle时是否会超出haystack长度的判断,但是这次问题是当第一个needle[0]找到后遍历途中发现不对重新找的时候出问题了,考虑了一下,是i++和return i的位置出了问题。
我觉得思路没有问题,只是实现的方式出了问题

for (int k = 0; k < n2; k++)
			{
				if (i + k == n1)
					return -1;
				if (haystack[i + k] != needle[k])
				{
					break;
				}
				return i;
			}
			i++;

在这段循环中,如果当遍历途中遇到不等于,则break出for循环,那么就会i++,但是当遇到相等时则会立刻return i,所以其实是失去了判断作用。

int strStr(char* haystack, char* needle) {
	int n1 = strlen(haystack);
	int n2 = strlen(needle);
	int i = 0;
	if (n2 == 0)
		return 0;
	if (n1 < n2)
		return -1;
	while (i < n1)
	{
		if (haystack[i] != needle[0])
		{
			i++;
		}
		else
		{
			for (int k = 0; k < n2; k++)
			{
				if (i + k == n1)
					return -1;
				if (haystack[i + k] != needle[k])
				{
					break;
				}
				if (k == n2 - 1)
					return i;
			}
			i++;
		}
	}
	return -1;
}

在这里插入图片描述
时间有点长,但是勉强能用。
下面是激动人心的看答案环节。
好家伙!
我只能说好家伙!!
不看不知道一看吓一跳,我这种暴力搜索的方法叫BF(Brute Force)算法,更高级一点的还有BM(Boyer-Moore)算法,原理是比较后缀,但是(相对)最优的是KMP(Knuth-Morris-Pratt)算法!
在这里插入图片描述
这个算法表述起来还挺复杂的,感觉leetcode官方答案里面讲的有点乱,
这个答案讲得不错:【动画模拟】这可能是全网最细的 KMP 讲解!(BF,BM,KMP)
但是还是这几个视频更容易看懂
天勤公开课」KMP算法易懂版
帮你把KMP算法学个通透!(理论篇)
帮你把KMP算法学个通透!(求next数组代码篇)

看完之后腰不酸腿不痛,但是代码还是不太会写,这里用c++试一试
关于求next[]数组的程序我按照视频讲的流程去写的

		for (int i = 1; i < needle.size(); i++)
		{
			while (j > 0 && needle[j] != needle[i])
			{
				j = next[j - 1];
			}
			if (needle[j] == needle[i])
			{
				j++; 
			}
			next[i] = j;

其实写完还是有点糊涂的,关于为什么当后缀结尾和前缀结尾发现不同元素时,j=next[j-1]的问题,我的理解是,当发现这一位不同时,其前面的都是经过比较的,所以没必要再作比较,直接跳到j的前一位对应的前缀的位置,再做比较。
注意这里的j>0是为了防止j-1得到负数从而使数组出错。
然后比较程序开始我是这样写的

		for (int i = 0, j = 0; i < haystack.size() - needle.size(); i++)
		{
			if (haystack[i] == needle[j])
			{
				j++;
			}
			else
			{
				j = next[j - 1];
			}
			if (j == needle.size())
				return i;
		}
		return -1;

运行起来发现不对,分析了一下,我这里当haystack[i] 不等于 needle[j]时,只进行了一次j=next[j-1]的运算,而实际上当判断不等时需要重复这个过程直到找到相同元素或确定没有相同元素为止,因此

		for (int i = 0, j = 0; i < haystack.size(); i++)
		{
			while (j > 0 && haystack[i] != needle[j])
			{
				j = next[j - 1];
			}
			if (haystack[i] == needle[j])
			{
				j++;
			}
			if (j == needle.size())
				return i - j + 1;
		}
		return -1;

这样就对了,这里while里面的j>0是为了让找到第一个相同的元素之后才开始使用next数组。
在这里插入图片描述
果然快了很多。
另外还有个地方

	int j = 0;
		if (needle.size() == 0)
			return 0;
		vector<int> next(needle.size());
		next[0] = 0;

这里大概vector不能创建长度为0的数组,因此如果把if放在vector后面,当needle长度为0时会报错。
完整代码

class Solution {
public:
	int strStr(string haystack, string needle) {
		int j = 0;
		if (needle.size() == 0)
			return 0;
		vector<int> next(needle.size());
		next[0] = 0;
		for (int i = 1; i < needle.size(); i++)
		{
			while (j > 0 && needle[j] != needle[i])
			{
				j = next[j - 1];
			}
			if (needle[j] == needle[i])
			{
				j++; 
			}
			next[i] = j;
		}
		for (int i = 0, j = 0; i < haystack.size(); i++)
		{
			while (j > 0 && haystack[i] != needle[j])
			{
				j = next[j - 1];
			}
			if (haystack[i] == needle[j])
			{
				j++;
			}
			if (j == needle.size())
				return i - j + 1;
		}
		return -1;
	}
};

在调程序的过程中遇到了一些问题,首先因为上次字符的问题,我打开了“Microsoft符号服务器”,然后今天不知道什么原因一运行程序就会开始下载各种dll文件,然后程序就卡在那里不动,我用这篇文章提到的方法关掉了它
VS2017 “正在从以下位置***加载符号”的解决办法
然后呢,因为我在程序没运行完就终止了,所以不知道出了什么错,一调试程序就显示
在这里插入图片描述
然后在这里找到了方法,其实就是强行结束任务。。。
关于1>LINK : fatal error LNK1168: 无法打开 …exe或者…dll进行写入的问题
就酱。

❤ 2021.9.18 ❤
今天的题目是

外观数列

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
又是很长的一个题目。。。
我的思路:就很简单嘛!写一个子函数将将输入字符串进行描述,然后将描述字符串输出,主函数n等于几就调用几次子函数。
话不多说

char* count(char* s,int* j)
{
	int len = strlen(s);
	int jishu=1;
	int i = 1;
	char* returnarray2 = (char*)malloc(sizeof(char)*len*2);
	*j = 0;
	returnarray2[0] = '1';
	returnarray2[1] = s[0];
	while (i < len)
	{
		if (s[i] == s[i - 1])
		{
			returnarray2[*j]++;
		}
		else
		{
			*j += 2;
			returnarray2[*j] = '1';
			returnarray2[*j+1] = s[i];
		}
		i++;
	}
	*j += 2;
	returnarray2 = (char*)realloc(returnarray2, sizeof(char) * (*j) + 1);
	returnarray2[sizeof(char) * (*j)] = 0;
	return returnarray2;
}

char* countAndSay(int n) {
	char* s = "1";
	char* returnarray = (char*)malloc(sizeof(char) * 2 + 1);
	returnarray[sizeof(char) * 2] = 0;
	int returnlen = 2;
	returnarray = count(s, &returnlen);
	if (n == 1)
		return "1";
	while (n-1 > 1)
	{
		returnarray = count(returnarray, &returnlen);
		returnarray = (char*)realloc(returnarray, sizeof(char) * returnlen + 1);
		returnarray[sizeof(char) * returnlen] = 0;
		n--;
	}
	return returnarray;
}

在这里插入图片描述
0ms就离谱。
其实在调试的时候我还是遇到一些问题的。
关于这个描述的子函数写起来倒是没什么困难,但是在这个内存分配上面纠结了半天,根据我的印象,字符串和字符数组的区别是字符串的最后一位是0,而字符数组并没有这个规定,用malloc和realloc函数来分配的实际上是字符数组,在输出字符串之前需要手动在字符串结尾加一个0。
见:
malloc得到字符串首地址的陷阱
为此我在子函数里仿照之前做过的题加了一个参数来表示返回字符串的长度。
然后又遇到一个问题,就是输入n=1的时候居然报错了。。。
好吧,输入为1的时候应该直接返回11才对。

然后看了看别人写的和答案,觉得这个哥们的代码非常简洁
在这里插入图片描述
简单分析了一下,首先他采用了递归的方法,其实想一想,这道题确实符合递归的特点,当n=1时输出是固定的,第n个输出取决于n-1的输入,然后他并没有像我一样在每次调用的时候重新分配内存,而是一开始就分配了足够的内存。嗯。。。这个算不算作弊。。。然后呢在返回值的结尾使字符数组的值为0,这样下次输入就会被当成字符串,妙啊!原来还可以这样,学到了学到了。。。

❤ 2021.9.24 ❤
今天的题目是:

最长公共前缀

在这里插入图片描述
我的思路:
芜湖又是个简单题。。。
不断地遍历字符串数组元素的前缀直到不同为止呗~
第一层循环用while,直到字符串中出现0为止,第二层循环不断遍历每个字符串的相同位置,先判断是否为0,再判断是否相等,全相等则returnstring相应位修改为对应字符,不相等则相应位写入0且跳出循环。

char* longestCommonPrefix(char** strs, int strsSize) {
	int i = 0, j = 0;
	int zero = 1;
	char* returnstring = (char*)malloc(sizeof(char) * 200);
	while (zero)
	{
		for (i = 0; i < strsSize; i++)
		{
			if (strs[i][j] == 0)
			{
				zero = 0;
				returnstring[j] = 0;
				break;
			}
			if (i != 0 && strs[i][j] != strs[i - 1][j])
			{
				break;
			}
		}
		if (zero == 1 && i == strsSize)
		{
			returnstring[j] = strs[0][j];
			j++;
		}
		if (zero == 1 && i != strsSize)
		{
			returnstring[j] = 0;
			return returnstring;
		}
	}
	return returnstring;
}

芜湖一次通过!
在这里插入图片描述
在这里我也取了个巧,直接把内存分配到了题目允许的上限,然后就不再调整。
在调试过程中还遇到了一个问题,我在给返回字符串分配内存后,先是赋给他一个空字符串,但是在后面重新赋值的时候却报错了。
在这里插入图片描述
后来我把赋值空字符串这句注释掉程序便通过了,看来赋值空字符串后就不能再赋值了(大概是这样吧。。。)
看答案。。。
答案给出了几种方法如下:
1、横向扫描:先比较前两个字符串,找到最长公共前缀,再将前缀和第三个元素比较,找到最大公共前缀,以此类推,直到结束或为空位置。
2、纵向扫描:也就是和我用的方法类似
在这里插入图片描述
我直接粘c++的了,首先他是按照第一个元素的长度来遍历的,的确,前缀最长不会超过第一个元素的长度,这点是我没想到的。其次,他也不是像我一样去判断是否为0,相对来说代码更加简洁一些。
3、二分法:先把第一个字符串二分为两份,先去判断前一份是否为前缀,如果是则把后一份再分为两份,如果不是则把前一份再分为两份,这样能够缩短时间。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值