力扣刷题笔记(六)

唉,昨天没有写这个系列的,被KMP给卡住了,不过还好,突破了。

照例三题,你们等下就会看到KMP算法了,实在是鬼斧生工。

第一题:实现strStr

实现 strStr() 函数。

给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。

示例 1:

输入: haystack = “hello”, needle = “ll”
输出: 2
示例 2:

输入: haystack = “aaaaa”, needle = “bba”
输出: -1
说明:

当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。

对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。

来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/implement-strstr
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


第二题:外观数列

「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。前五项如下:

  1. 1
    
  2. 11
    
  3. 21
    
  4. 1211
    
  5. 111221
    

1 被读作 “one 1” (“一个一”) , 即 11。
11 被读作 “two 1s” (“两个一”), 即 21。
21 被读作 “one 2”, “one 1” (“一个二” , “一个一”) , 即 1211。

给定一个正整数 n(1 ≤ n ≤ 30),输出外观数列的第 n 项。

注意:整数序列中的每一项将表示为一个字符串。

示例 1:

输入: 1
输出: “1”
解释:这是一个基本样例。
示例 2:

输入: 4
输出: “1211”
解释:当 n = 3 时,序列是 “21”,其中我们有 “2” 和 “1” 两组,“2” 可以读作 “12”,也就是出现频次 = 1 而 值 = 2;类似 “1” 可以读作 “11”。所以答案是 “12” 和 “11” 组合在一起,也就是 “1211”。

来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/count-and-say
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


第三题:atoi实现

请你来实现一个 atoi 函数,使其能将字符串转换成整数。

首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。接下来的转化规则如下:

如果第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字字符组合起来,形成一个有符号整数。
假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成一个整数。
该字符串在有效的整数部分之后也可能会存在多余的字符,那么这些字符可以被忽略,它们对函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换,即无法进行有效转换。

在任何情况下,若函数不能进行有效的转换时,请返回 0 。

提示:

本题中的空白字符只包括空格字符 ’ ’ 。
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。

示例 1:

输入: “42”
输出: 42
示例 2:

输入: " -42"
输出: -42
解释: 第一个非空白字符为 ‘-’, 它是一个负号。
我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。
示例 3:

输入: “4193 with words”
输出: 4193
解释: 转换截止于数字 ‘3’ ,因为它的下一个字符不为数字。
示例 4:

输入: “words and 987”
输出: 0
解释: 第一个非空字符是 ‘w’, 但它不是数字或正、负号。
因此无法执行有效的转换。
示例 5:

输入: “-91283472332”
输出: -2147483648
解释: 数字 “-91283472332” 超过 32 位有符号整数范围。
因此返回 INT_MIN (−231) 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/string-to-integer-atoi
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


我的题解(1) --KMP算法

总算是彻底弄清楚了,KMP算法

代码就不放这篇了,放了也基本看不懂,解释的话没几千字解释不清楚,所以我就把我的KMP博客链接放这儿吧。


我的题解(2)–递归

我的好兄弟他跟我说看不懂这道题,让我赶紧解决KMP之后过来,我一看,这不是很简单吗!!!

这么说把,这道题,不管你给的数字是多少,最后肯定要回到1,从1出发。也不要想着什么暴力算法,我试过了,给你个9,你就得输出14个字符,那给你个30你是不是得输出上百个字符啊。

既然都要回到1,又是重1出发一层一层嵌套,那很直观的想法就是递归嘛。说实话这是我为数不多的用递归的情况,之前在快排、上面那个KMP用到,好像就没了。

代码中已经带上了我的思考,当然,老规矩,流程图不能少。

在这里插入图片描述

#include<iostream>
#include<string>
#include<vector>

using namespace std;

//先描述一个简单的字符串吧
string show(string str,int n)
{	
	//递归结束条件
	if (n == 0)
	{
		return str;
	}

	int fast = 0, slow = 0;//快慢指针
	int sz = str.size();
	int count = 0;
	string new_str;

	while(slow < sz)
	{	
		if (fast<sz && str[fast] == str[slow])
		{
			count++;
			fast++;
		}
		else
		{
			new_str.push_back(count+48);
			new_str.push_back(str[slow]);
			count = 0;
			slow = fast;
		}
	}
	n--;
	new_str = show(new_str,n);	//前面不是return了?为什么会绕回来这里?哦,递归,原来是这样,只是第二层return了,当然回到前一层
	return new_str;
}

string countAndSay(int n) {
	if (n == 1)
		return "1";
	else
	{
		string s = "1";
		string t = show(s, n-1);	//这里的n对应的是n+1,因为n=1是个特殊情况
		return t;
	}
}

int main()
{
	string a = countAndSay(2);
	cout << a << endl;
}

我的题解(3)

这题其实没什么难的,就是细节很多,所以排在了中等难度,我就拿过来了。
唉,忏愧,提交了12次。

#include<iostream>
#include<string>

using namespace std;

int myAtoi(string str) 
{
	int sz = str.size();
	string str_temp;
	int zf_flag = 1;	//正数or复数//emmm,或者就一个正号

	for (int a = 0; a < sz; a++)
	{
		//进入取值地界了
		if (str[a] != 32)
		{
			//不是从这里进,从第一个非空格1进
			if (str[a] == 45 || str[a] == 43 || (str[a] >= 48 && str[a] <= 57))
			{
				if (str[a] == 45)
				{
					zf_flag = -1;
					a++;
				}
				else if (str[a] == 43)
				{
					zf_flag = 0;
					a++;
				}

				while(str[a] == 48)	//把0滤掉
					a++;
				
				while (str[a] >= 48 && str[a] <= 57)
				{
					str_temp.push_back(str[a]);
					a++;
				}
				break;	//退出循环
			}
			else
				break;
		}
	}

	//接下来对取出的值进行操作

	int temp_size = str_temp.size();
	int ret = 0;
	int i = 0;
	int pow_num = 0;
	//对负数进行操作
	if (zf_flag == -1)
	{
		if (temp_size == 0)
			return 0;

		if (temp_size < 10)
		{
			while (temp_size > 0)
			{
				temp_size--;
				pow_num = pow(10, temp_size);
				ret += (int)(str_temp[i] - 48) * pow_num;
				i++;
			}

			return (-1)*ret;
		}
		//判断临界条件
		else if (temp_size == 10)
		{
			if (str_temp[0] > 50)
				return INT_MIN;

			ret = INT_MIN;
			while (temp_size > 0)
			{
				temp_size--;
				pow_num = pow(10, temp_size);
				ret += (int)(str_temp[i] - 48) * pow_num;
				i++;
			}

			if (ret <= 0)
				return (-1)*(ret - INT_MIN);
			else
				return INT_MIN;
		}
		else
			return INT_MIN;
	}

	//对正数进行操作
	if (zf_flag == 1 || zf_flag == 0)
	{
		if (temp_size == 0)
			return 0;
		if (temp_size < 10)
		{
			while (temp_size > 0)
			{
				temp_size--;
				pow_num = pow(10, temp_size);
				ret += (int)(str_temp[i] - 48) * pow_num;
				i++;
			}

			return ret;
		}
		else if (temp_size == 10)
		{
			if(str_temp[0]>50)
				return INT_MAX;

			ret = INT_MAX;
			while (temp_size > 0)
			{
				temp_size--;
				pow_num = pow(10, temp_size);
				ret -= (int)(str_temp[i] - 48) * pow_num;
				i++;
			}

			if (ret <= 0)
				return INT_MAX;
			else
				return (INT_MAX-ret);
		}
		else
			return INT_MAX;
	}

	return 0;
}

int main()
{
	//int a = '9';	//57
	//int b = '0';	//48
	//int c = '-';	//45
	//int d = ' ';	//32
	//int e = '+';	//43
	int d = myAtoi("2147483633");
	cout << d << endl;
	return 0;
}

官方题解(1) – Rabin Karp - 常数复杂度

方法恒强,反正我是没看懂。上次(前天)说要搞定哈希表之后,就被快排拖住了,解决了快排之后,又让KMP卡住了,所以哈希表我到现在还不会用。所以就看不懂了。

有一种最坏时间复杂度也为 O(N)O(N) 的算法。思路是这样的,先生成窗口内子串的哈希码,然后再跟 needle 字符串的哈希码做比较。

这个思路有一个问题需要解决,如何在常数时间生成子串的哈希码?

在这里插入图片描述

在这里插入图片描述

算法

计算子字符串 haystack.substring(0, L) 和 needle.substring(0, L) 的哈希值。

从起始位置开始遍历:从第一个字符遍历到第 N - L 个字符。

根据前一个哈希值计算滚动哈希。

如果子字符串哈希值与 needle 字符串哈希值相等,返回滑动窗口起始位置。

返回 -1,这时候 haystack 字符串中不存在 needle 字符串。

实现

PythonJava

class Solution:
    def strStr(self, haystack: str, needle: str) -> int:
        L, n = len(needle), len(haystack)
        if L > n:
            return -1
        
        # base value for the rolling hash function
        a = 26
        # modulus value for the rolling hash function to avoid overflow
        modulus = 2**31
        
        # lambda-function to convert character to integer
        h_to_int = lambda i : ord(haystack[i]) - ord('a')
        needle_to_int = lambda i : ord(needle[i]) - ord('a')
        
        # compute the hash of strings haystack[:L], needle[:L]
        h = ref_h = 0
        for i in range(L):
            h = (h * a + h_to_int(i)) % modulus
            ref_h = (ref_h * a + needle_to_int(i)) % modulus
        if h == ref_h:
            return 0
              
        # const value to be used often : a**L % modulus
        aL = pow(a, L, modulus) 
        for start in range(1, n - L + 1):
            # compute rolling hash in O(1) time
            h = (h * a - h_to_int(start - 1) * aL + h_to_int(start + L - 1)) % modulus
            if h == ref_h:
                return start

        return -1

复杂度分析

时间复杂度:O(N)O(N),计算 needle 字符串的哈希值需要 O(L)O(L) 时间,之后需要执行 (N - L)(N−L) 次循环,每次循环的计算复杂度为常数。

空间复杂度:O(1)O(1)。

作者:LeetCode
链接:https://leetcode-cn.com/problems/implement-strstr/solution/shi-xian-strstr-by-leetcode/
来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

难搞。


官方题解(2)

无,我的代码时空复杂度都超过百分百的提交了。看我的吧。


官方题解(3) 有限状态机(DFA)

难怪难度排到中等。

方法一:自动机
思路

字符串处理的题目往往涉及复杂的流程以及条件情况,如果直接上手写程序,一不小心就会写出极其臃肿的代码。

因此,为了有条理地分析每个输入字符的处理方法,我们可以使用自动机这个概念:

我们的程序在每个时刻有一个状态 s,每次从序列中输入一个字符 c,并根据字符 c 转移到下一个状态 s’。这样,我们只需要建立一个覆盖所有情况的从 s 与 c 映射到 s’ 的表格即可解决题目中的问题。

算法

本题可以建立如下图所示的自动机:
在这里插入图片描述
我们也可以用下面的表格来表示这个自动机:

接下来编程部分就非常简单了:我们只需要把上面这个状态转换表抄进代码即可。

另外自动机也需要记录当前已经输入的数字,只要在 s’ 为 in_number 时,更新我们输入的数字,即可最终得到输入的数字。

在这里插入图片描述

INT_MAX = 2 ** 31 - 1
INT_MIN = -2 ** 31

class Automaton:
    def __init__(self):
        self.state = 'start'
        self.sign = 1
        self.ans = 0
        self.table = {
            'start': ['start', 'signed', 'in_number', 'end'],
            'signed': ['end', 'end', 'in_number', 'end'],
            'in_number': ['end', 'end', 'in_number', 'end'],
            'end': ['end', 'end', 'end', 'end'],
        }
        
    def get_col(self, c):
        if c.isspace():
            return 0
        if c == '+' or c == '-':
            return 1
        if c.isdigit():
            return 2
        return 3

    def get(self, c):
        self.state = self.table[self.state][self.get_col(c)]
        if self.state == 'in_number':
            self.ans = self.ans * 10 + int(c)
            self.ans = min(self.ans, INT_MAX) if self.sign == 1 else min(self.ans, -INT_MIN)
        elif self.state == 'signed':
            self.sign = 1 if c == '+' else -1

class Solution:
    def myAtoi(self, str: str) -> int:
        automaton = Automaton()
        for c in str:
            automaton.get(c)
        return automaton.sign * automaton.ans


作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/string-to-integer-atoi/solution/zi-fu-chuan-zhuan-huan-zheng-shu-atoi-by-leetcode-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

总结

这些题做完,我们应该掌握递归算法,熟悉KMP算法,然后准备去学DFA了!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章