唉,昨天没有写这个系列的,被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
-
11
-
21
-
1211
-
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博客链接放这儿吧。
我的题解(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了!!!