优雅的字符串动态规划。
class Solution {
public int minDistance(String word1, String word2) {
int len1 = word1.length();
int len2 = word2.length();
// dp[i][j]的含义是,word1和word2转化为相同需要的删除操作次数
int[][] dp = new int[len1 + 1][len2 + 1];
// 初始化
for (int i = 1; i <= len1; i++) {
dp[i][0] = i;
}
for (int j = 1; j <= len2; j++) {
dp[0][j] = j;
}
// 状态转移
for (int i = 1; i <= len1; i++) {
for (int j = 1; j <= len2; j++) {
if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + 1;
}
}
}
return dp[len1][len2];
}
}
两个矩形的位置关系多种多样,很难分类讨论。下面是最优雅的解法。
class Solution {
public int computeArea(int ax1, int ay1, int ax2, int ay2, int bx1, int by1, int bx2, int by2) {
int s1 = (ax2 - ax1) * (ay2 - ay1);
int s2 = (bx2 - bx1) * (by2 - by1);
int w = Math.min(ax2, bx2) - Math.max(ax1, bx1);
int h = Math.min(ay2, by2) - Math.max(ay1, by1);
int s3 = Math.max(w, 0) * Math.max(h, 0);
return s1 + s2 - s3;
}
}
这是一个数据流形式的扩展区间/合并区间问题。看似复杂,其实只有5种情况。
假设当前数字为val,[l0, r0]为其左侧区间,[l1, r1]为其右侧区间:
情况一:"l0 <= val <= r0"。val包含在区间内部,无需进行任何操作。
情况二:"r0 + 1 = val"。左侧区间向右扩展一小步。
情况三:"l1 - 1 = val"。右侧区间向左扩展一小步。
情况四:同时满足情况二和情况三,区间发生合并。
情况五:上述情况都不满足,则独立形成一个新的区间。
技巧>_<
用TreeMap<Integer, Integer>数据结构来表示众多区间。
一来自带排序,二来可以使用floorEntry()和ceilingEntry()方法轻易获取左右区间。
class SummaryRanges {
private TreeMap<Integer, Integer> intervals;
public SummaryRanges() {
intervals = new TreeMap<>();
}
public void addNum(int val) {
// 左侧区间/右侧区间
Map.Entry<Integer, Integer> interval0 = intervals.floorEntry(val);
Map.Entry<Integer, Integer> interval1 = intervals.ceilingEntry(val + 1);
// 区间边界
// int l0 = interval0.getKey(), r0 = interval0.getValue();
// int l1 = interval1.getKey(), r1 = interval1.getValue();
// 分情况讨论
if (interval0 != null && interval0.getKey() <= val && val <= interval0.getValue()) {
// 情况一(val包含在区间内部,无需进行任何操作)
return;
} else {
boolean extendL = interval0 != null && interval0.getValue() + 1 == val;
boolean extendR = interval1 != null && interval1.getKey() - 1 == val;
if (extendL && extendR) {
// 情况四(同时满足情况二和情况三,区间发生合并)
int l = interval0.getKey();
int r = interval1.getValue();
intervals.remove(interval0.getKey());
intervals.remove(interval1.getKey());
intervals.put(l, r);
} else if (extendL) {
// 情况二(左侧区间向右扩展一小步)
intervals.put(interval0.getKey(), interval0.getValue() + 1);
} else if (extendR) {
// 情况三(右侧区间向左扩展一小步)
int k = interval1.getKey();
int v = interval1.getValue();
intervals.remove(k);
intervals.put(k - 1, v);
} else {
// 情况五(上述情况都不满足,则独立形成一个新的区间)
intervals.put(val, val);
}
}
}
public int[][] getIntervals() {
// 将TreeMap数据结构转化为int[][]数据结构
int len = intervals.size();
int idx = 0;
int[][] res = new int[len][2];
for (Map.Entry<Integer, Integer> interval : intervals.entrySet()) {
res[idx][0] = interval.getKey();
res[idx][1] = interval.getValue();
idx++;
}
return res;
}
}
检索标记:352. 将数据流变为多个不相交区间给你一个由非负整数 a1, a2, …, an 组成的数据流输入,请你将到目前为止看到的数字总结为不相交的区间列表。实现 SummaryRanges 类SummaryRanges() 使用一个空数据流初始化对象。void addNum(int val) 向数据流中加入整数 val 。int[][] getIntervals() 以不相交区间 [starti, endi] 的列表形式返回对数据流中整数的总结。223. 矩形面积给你 二维 平面上两个 由直线构成的 矩形,请你计算并返回两个矩形覆盖的总面积。每个矩形由其 左下 顶点和 右上 顶点坐标表示:第一个矩形由其左下顶点 (ax1, ay1) 和右上顶点 (ax2, ay2) 定义。第二个矩形由其左下顶点 (bx1, by1) 和右上顶点 (bx2, by2) 定义。583. 两个字符串的删除操作给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符。示例:输入: “sea”, “eat"输出: 2解释: 第一步将"sea"变为"ea”,第二步将"eat"变为"ea"