0030题 与所有单词相关联的字串【Substring with Concatenation of All Words】
题目:
给定一个字符串 s 和一些长度相同的单词 words。在 s 中找出可以恰好串联 words 中所有单词的子串的起始位置。
注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。
示例:
输入:
s = "barfoothefoobarman",
words = ["foo","bar"]
输出: [0,9]
解释: 从索引 0 和 9 开始的子串分别是 "barfoo" 和 "foobar" 。
输出的顺序不重要, [9,0] 也是有效答案。
题目不严谨之处:
1. words应该是不会有空字符串出现的,结果输入数据仍然有
2. 题目的解释中多了一个‘r’,在本博客中已经修改并删掉
3. 题目中说到words中的单词长度应该是统一的,示例2的words无法满足题目条件,在本博客中已经修改并删掉。
需要注意:
1. 不知道示例2是怎么回事,如果出现不统一长的wordsList,那么应该直接反馈空List
2. 没有说明words中不允许出现相同的单词,所以仍然需要注意
3. Robust
解题思路:
一开始没有想到特别好的思路,尝试暴力搜索,代码如下:
class Solution:
def __init__(self):
self.wordsList = {}
def search(self, s, start, end, l):
tmp = self.wordsList.copy()
for i in range(start, end, l):
local = s[i:i+l]
if tmp.get(local):
if tmp[local] > 1:
tmp[local] -= 1
else:
del(tmp[local])
if len(tmp) == 0:
return True
else:
return False
def findSubstring(self, s, words):
if len(s) == 0 or len(words) == 0: return []
l = len(words[0])
cnt = len(words)
res = []
for word in words:
if self.wordsList.get(word):
self.wordsList[word] += 1
else:
self.wordsList[word] = 1
for i in range(0, len(s)):
if self.search(s, i, i+cnt*l, l):
res.append(i)
return res
emm,然后这段代码打进去之后,竟然才爆了两个点,点进去一看那个数据……立即加一下针对样例的“优化”
if len(self.wordsList) == 1 and self.wordsList.get('a'):
for i in range(0, len(s)-cnt+1):
res.append(i)
return res
其实看了一波提交的答案,发现也都大同小异……之前也说过追求绝对速度是不可取的,所以这里也直接不管了,理论上来讲这道题一定有一些比较好的数据结构可以处理,但是没有编,然后看了一下解答也并没有,所以如果有那位大佬哪天突然有兴致想编的话,可以在下面评论。
0032题 最长有效括号【Longest Valid Parentheses】
题目:
给定一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长的包含有效括号的子串的长度。
示例:
输入: "(()"
输出: 2
解释: 最长有效括号子串为 "()"
输入: ")()())"
输出: 4
解释: 最长有效括号子串为 "()()"
题目相对严谨
除了Robust以外,无需注意太多
解题思路:
1.题目越短的题目,难度往往都略高一些,因为没有办法从题目中获取更多的信息,自初中正式面对考试时,这种规律就没有变过。这道题也是,看似非常容易理解的题目,但是想起来确实费一番功夫。
这题暴力搜索复杂度应该是
O(n3)
O
(
n
3
)
,粗略估计了一下,不过肯定不能使用了,然后觉得括号匹配的题还是用栈来实现,就简单编了一个短代码,如下:
class Solution:
def longestValidParentheses(self, s):
res = 0
tmp_valid = 0
p = list()
for i in range(len(s)):
if s[i] == '(':
p.append(s[i])
else:
if len(p) == 0:
res = max(res, tmp_valid)
tmp_valid = 0
elif p[-1] == '(':
tmp_valid += 2
p.pop()
res = max(res, tmp_valid)
return res
不过很明显这个是错误的代码,错误样例如“()(()”
,想到这种错误样例,立马意识到是因为这段代码过于早的pop并计算了当下可能最长长度,所以应该在既保留当下可能最长长度,又应该一直维持并没有匹配的左括号,这里就要注意新的两个样例:“()((()”,“()((())”
class Solution:
def longestValidParentheses(self, s):
res = 0
p = list()
for i in range(len(s)):
if s[i] == '(':
p.append(s[i])
else:
if len(p) == 0: continue
if p[-1] == '(':
if len(p) == 1:
p.pop()
p.append(str(2))
else:
tmp = 2
p.pop()
while len(p) > 0 and p[-1].isdigit(): tmp += int(p.pop())
p.append(str(tmp))
elif p[-1].isdigit():
if len(p) == 1:
res = max(res, int(p[-1]))
p = list()
else:
tmp = int(p.pop()) + 2
p.pop()
while len(p) > 0 and p[-1].isdigit(): tmp += int(p.pop())
p.append(str(tmp))
for c in p:
if c.isdigit():
res = max(res, int(c))
return res
2.这样做无非是在不动脑子的模拟操作,这道题的提示中有着动态规划四个字,思来想去,觉得如果是DP的话,更新点肯定是在右括号,那就是字符串中有什么规律可循,参考网上的解答贴:https://blog.csdn.net/accepthjp/article/details/52439449,才发现如下规律:
dp[i]表示以当前位置为终点的最长长度,则只能在)处更新,
如果s[i-1-dp[i-1]]==’(‘,则说明当前位置可以和i-1-dp[i-1]位置匹配,dp[i]=dp[i-1]+2
然后还要加上匹配位置之前的最长长度dp[i]+=dp[i-dp[i]]
自己在草稿纸上演算了一下,还真是这个道理,有点自叹不如……附上最后的代码:
class Solution:
def longestValidParentheses(self, s):
max_len = 0
dp = [-1]
for i in range(len(s)):
if s[i] == '(':
dp.append(i)
else:
dp.pop()
if(len(dp) == 0):
dp.append(i)
else:
max_len = max(max_len, i - dp[-1])
return max_len
0036题 有效的数独【Valid Sudoku】
题目:
判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。
- 数字 1-9 在每一行只能出现一次。
- 数字 1-9 在每一列只能出现一次。
- 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。
说明:
- 一个有效的数独(部分已被填充)不一定是可解的。
- 只需要根据以上规则,验证已经填入的数字是否有效即可。
- 给定数独序列只包含数字 1-9 和字符 ‘.’ 。
- 给定数独永远是 9x9 形式的。
示例:
输入:
[
["5","3",".",".","7",".",".",".","."],
["6",".",".","1","9","5",".",".","."],
[".","9","8",".",".",".",".","6","."],
["8",".",".",".","6",".",".",".","3"],
["4",".",".","8",".","3",".",".","1"],
["7",".",".",".","2",".",".",".","6"],
[".","6",".",".",".",".","2","8","."],
[".",".",".","4","1","9",".",".","5"],
[".",".",".",".","8",".",".","7","9"]
]
输出: true
输入:
[
["8","3",".",".","7",".",".",".","."],
["6",".",".","1","9","5",".",".","."],
[".","9","8",".",".",".",".","6","."],
["8",".",".",".","6",".",".",".","3"],
["4",".",".","8",".","3",".",".","1"],
["7",".",".",".","2",".",".",".","6"],
[".","6",".",".",".",".","2","8","."],
[".",".",".","4","1","9",".",".","5"],
[".",".",".",".","8",".",".","7","9"]
]
输出: false
解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。
但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。
题目相对严谨
无需注意太多
解题思路:
【密恐症人慎入的几道题(棒读)】
这题仅仅是判断数独是否有效,而并不是判断数独可否有解,所以工作量顿时降低。
简单模拟题目就好,这里不放上那么冗余的代码了。
0037题 解数独【Sudoku Solver】
题目:
编写一个程序,通过已填充的空格来解决数独问题。
一个数独的解法需遵循如下规则:
- 数字 1-9 在每一行只能出现一次。
- 数字 1-9 在每一列只能出现一次。
- 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
说明:
- 一个有效的数独(部分已被填充)不一定是可解的。
- 只需要根据以上规则,验证已经填入的数字是否有效即可。
- 给定数独序列只包含数字 1-9 和字符 ‘.’ 。
- 给定数独永远是 9x9 形式的。
空白格用 ‘.’ 表示。
题目相对严谨
经典题目无需注意太多
解题思路:
这是所有搜索题目的经典题,DFS或者BFS都可以解,然后就想到当年看到解数独时候的Dancing Links……舞蹈链算法并不是在这里用的,会爆炸。
因为是经典回溯搜索题目,所以就不post代码了。
0038题 报数【Count and Say】
题目:
报数序列是指一个整数序列,按照其中的整数的顺序进行报数,得到下一个数。其前五项如下:
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 ,输出报数序列的第 n 项。
注意:整数顺序将表示为一个字符串。
示例:
输入: 1
输出: "1"
输入: 4
输出: "1211"
题目相对严谨
除Robust以外,无需注意太多。
解题思路:
emmm,一开始看题感觉有点懵,不知道在说什么,后来想想无非还是模拟题。
post代码:
class Solution:
def nxt(self, s):
ss = ""
n = len(s)
i = 0
while i < n:
tmp = i + 1
while tmp < n and s[tmp] == s[i]: tmp += 1
ss += str(tmp - i) + s[i]
i = tmp
return ss
def countAndSay(self, n):
res = ['1']
for i in range(1, n):
res.append(self.nxt(res[-1]))
return res[-1]
如果有数学解法的话,欢迎评论……
0039题 组合总和【Combination Sum】
题目:
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
- 所有数字(包括 target)都是正整数。
- 解集不能包含重复的组合。
示例:
输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
输入: candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]
题目相对严谨
除Robust以外无需注意太多
解题思路:
好吧,这是一道再经典不过的回溯题目了,直接AC掉,无需post代码