Algorithm
本周选择的算法题是:Implement strStr()
规则如下:
Implement strStr().
Return the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack.
Example 1:
Input: haystack = "hello", needle = "ll"
Output: 2
Example 2:
Input: haystack = "aaaaa", needle = "bba"
Output: -1
Clarification:
What should we return when needle
is an empty string? This is a great question to ask during an interview.
For the purpose of this problem, we will return 0 when needle
is an empty string. This is consistent to C’s strstr() and Java’s indexOf().
Constraints:
haystack
andneedle
consist only of lowercase English characters.
Solution
KMP 算法的二维数组版:
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
m = len(needle)
if m == 0: return 0
# 构建状态表
dp = [[0] * (ord('z') - ord('a') + 1) for _ in range(m)]
dp[0][ord(needle[0]) - ord('a')] = 1
x = 0
for j in range(1, m):
for c in range(ord('z') - ord('a')):
if needle[j] == chr(ord('a') + c):
dp[j][c] = j + 1
else:
dp[j][c] = dp[x][c]
x = dp[x][ord(needle[j]) - ord('a')]
# 开始状态推进
j = 0
for i, s in enumerate(haystack):
j = dp[j][ord(s) - ord('a')]
if j == m: return i - m + 1
return -1
相比传统的 KMP 算法:
- 该版本需要更多的空间,但近似空间复杂度是一样的:O(26M)=O(M)
- 比一维数组更好理解状态的变化
- 时间复杂度都是 O(n)
传统的 KMP 算法:
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
n, m = len(haystack), len(needle)
if m == 0: return 0
if n == 0: return -1
# 构建状态回退表
dp = [0] * m
j = 0
for i in range(1, m):
if j > 0 and needle[i] != needle[j]:
j = dp[j-1]
if needle[i] == needle[j]:
j += 1
dp[i] = j
# 开始匹配
i, j = 0, 0
while i < n:
while j > 0 and haystack[i] != needle[j]:
j = dp[j-1]
if haystack[i] == needle[j]:
j += 1
i += 1
if j == m: return i - j
return -1
状态的变化过程不太容易理解。
双指针暴力解法:
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
n, m = len(haystack), len(needle)
if m == 0: return 0
if n == 0: return -1
p1, p2, curr_len = 0, 0, 0
while p1 < n and p2 < m:
if haystack[p1] == needle[p2]:
curr_len += 1
p1 += 1
p2 += 1
else:
p1 = p1 - curr_len + 1
p2, curr_len = 0, 0
if curr_len == m: return p1-p2
return -1
Review
iOS 14: What is new for WKWebView 苹果还在持续不断地改进 WKWebView
,这次在 iOS 14 中:
- 增加了 JavaScript 沙盒模式的代码注入,隔离了宿主的执行环境
- 增加了新的原生调用 JS 的 API:
callAsyncJavaScript
- 增加了全文检索 API:
findString
- 支持将网页导出成 PDF
- 支持在原生控制页面的缩放
Tip
Behind the scene of ETag caching 偶然发现 OSS 服务器对部分资源没有返回 ETag 响应头,在网上找到了这篇文章。
打算再找机会看看 ETag 的计算逻辑,主要需要了解:
- 为什么设置了
Accept-Encoding: gzip, deflate, br
后会返回Transfer-Encoding: chunk
- 为什么导致了 ETag 没有被计算
Share
在探索 Swift ARC 的过程中对 SIL 做了一些了解,发现 SIL 是很容易的理解的,比如以下源码:
class aClass{
var value = 1
}
func main() {
var c1 = aClass()
var c2 = aClass()
var fSpec = {
[unowned c1, weak c2] in
c1.value = 42
if let c2o = c2 {
c2o.value = 42
}
}
fSpec()
}
main()
关注到这一行:
var c1 = aClass()
它对应的 SIL 语句为:
%0 = alloc_stack $aClass, var, name "c1" // users: %5, %52, %51, %14
%1 = metatype $@thick aClass.Type // user: %3
// function_ref aClass.__allocating_init()
%2 = function_ref @test.aClass.__allocating_init() -> test.aClass : $@convention(method) (@thick aClass.Type) -> @owned aClass // user: %3
%3 = apply %2(%1) : $@convention(method) (@thick aClass.Type) -> @owned aClass // users: %20, %16, %5, %4
strong_retain %3 : $aClass // id: %4
store %3 to %0 : $*aClass // id: %5
...
strong_release %3 : $aClass // id: %20
...
destroy_addr %0 : $*aClass // id: %51
dealloc_stack %0 : $*aClass // id: %52
苹果提供了一分 SIL 指令文档,查询后可知:
alloc_stack - 在栈上分配一段未初始化的内存,也就是 %0
strong_retain - 增加了对象的 strong count
- store - 将 %3 存放内存 %0,也就是之前分配的栈的地址
- strong_release - 减少对象的 strong count,当 strong count 和 unowned count 都为0时,清理对象内存
- destroy_addr - 清理对象内存
- dealloc_stack - 回收之前通过
alloc_stack
分配的内存,在调用这个指令前,内存必须是未初始化或清除的状态
通过这种方式可以对 Swift 编译器背后针对 ARC 的实现有一个清晰的了解。