【宫水三叶】简单模拟题
模拟
由于每次旋转操作都是将最左侧字符移动到最右侧,因此如果 goal 可由 s 经过多步旋转而来,那么 goal 必然会出现在 s + s 中,即满足 (s + s).contains(goal),同时为了 s 本身过长导致的结果成立,我们需要先确保两字符串长度相等。
代码:
###Java
class Solution {
public boolean rotateString(String s, String goal) {
return s.length() == goal.length() && (s + s).contains(goal);
}
}
- 时间复杂度:$O(n)$
- 空间复杂度:$O(n)$
关于 contains 操作的复杂度说明
看到不少同学对 contains 的复杂度写成 $O(n)$ 有疑问。
在 Java 中,contains 实际最终是通过 indexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex) 来拿到子串在原串的下标,通过判断下标是否为 $-1$ 来得知子串是否在原串出现过。
我们知道一个较为普通的子串匹配算法的复杂度通为 $O(n*k)$,其中 $n$ 和 $k$ 分别是原串和子串的长度,而一些复杂度上较为优秀的算法可以做到 $O(n + k)$,例如 KMP。
从复杂度上来看 KMP 似乎要更好,但实际上对于 indexOf 这一高频操作而言,KMP 的预处理逻辑和空间开销都是不可接受的。
因此在 OpenJDK 中的 indexOf 源码中,你看不到诸如 KMP 这一类「最坏情况下仍为线性复杂度」的算法实现。
但是 contains 的复杂度真的就是 $O(n * k)$ 吗?
其实并不是,这取决于 JVM 是否有针对 indexOf 的优化,在最为流行 HotSpot VM 中,就有对 indexOf 的优化。
使用以下两行命令执行 Main.java,会得到不同的用时。
###Java
// Main.java
import java.util.*;
class Main {
static String ss = "1900216589537958049456207450268985232242852754963049829410964867980510717200606495004259179775210762723370289106970649635773837906542900276476226929871813370344374628795427969854262816333971458418647697497933767559786473164055741512717436542961770628985635269208255141092673831132865";
static String pp = "830411595466023844647269831101019568881117264597716557501027220546437084223034983361631430958163646150071031688420479928498493050624766427709034028819288384316713084883575266906600102801186671777455503932259958027055697399984336592981698127456301551509241";
static int cnt = (int) 1e8;
static public void main(String[] args) {
long start = System.currentTimeMillis();
while (cnt-- > 0) ss.contains(pp);
System.out.println(System.currentTimeMillis() - start);
}
}
环境说明:
###Shell
➜ java -version
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
先执行 javac Main.java 进行编译后:
- 使用原始的
indexOf实现进行匹配(执行多次,平均耗时为基准值 $X$):
java -XX:+UnlockDiagnosticVMOptions -XX:DisableIntrinsic=_indexOf Main
- 使用 HotSpot VM 优化的
indexOf进行匹配(执行多次,平均耗时为基准值 $X$ 的 $[0.55, 0.65]$ 之间):
java Main
因此实际运行的 contains 操作的复杂度为多少并不好确定,但可以确定是要优于 $O(n * k)$ 的。
加练
今日份加餐 :【面试高频题】难度 2/5,结合二分的序列 DP 运用题 🍭🍭🍭🍭
或是考虑加练如下「模拟」题 🍭🍭🍭
| 题目 | 题解 | 难度 | 推荐指数 |
|---|---|---|---|
| 6. Z 字形变换 | LeetCode 题解链接 | 中等 | 🤩🤩🤩 |
| 8. 字符串转换整数 (atoi) | LeetCode 题解链接 | 中等 | 🤩🤩🤩 |
| 12. 整数转罗马数字 | LeetCode 题解链接 | 中等 | 🤩🤩 |
| 59. 螺旋矩阵 II | LeetCode 题解链接 | 中等 | 🤩🤩🤩🤩 |
| 65. 有效数字 | LeetCode 题解链接 | 困难 | 🤩🤩🤩 |
| 73. 矩阵置零 | LeetCode 题解链接 | 中等 | 🤩🤩🤩🤩 |
| 89. 格雷编码 | LeetCode 题解链接 | 中等 | 🤩🤩🤩🤩 |
| 166. 分数到小数 | LeetCode 题解链接 | 中等 | 🤩🤩🤩🤩 |
| 260. 只出现一次的数字 III | LeetCode 题解链接 | 中等 | 🤩🤩🤩🤩 |
| 414. 第三大的数 | LeetCode 题解链接 | 中等 | 🤩🤩🤩🤩 |
| 419. 甲板上的战舰 | LeetCode 题解链接 | 中等 | 🤩🤩🤩🤩 |
| 443. 压缩字符串 | LeetCode 题解链接 | 中等 | 🤩🤩🤩🤩 |
| 457. 环形数组是否存在循环 | LeetCode 题解链接 | 中等 | 🤩🤩🤩🤩 |
| 528. 按权重随机选择 | LeetCode 题解链接 | 中等 | 🤩🤩🤩🤩 |
| 539. 最小时间差 | LeetCode 题解链接 | 中等 | 🤩🤩🤩🤩 |
| 726. 原子的数量 | LeetCode 题解链接 | 困难 | 🤩🤩🤩🤩 |
注:以上目录整理来自 wiki,任何形式的转载引用请保留出处。
最后
如果有帮助到你,请给题解点个赞和收藏,让更多的人看到 ~ ("▔□▔)/
也欢迎你 关注我 和 加入我们的「组队打卡」小群 ,提供写「证明」&「思路」的高质量题解。
所有题解已经加入 刷题指南,欢迎 star 哦 ~