diff --git a/Leecode/src/main/java/com/markilue/leecode/hot100/T86_DecodeString.java b/Leecode/src/main/java/com/markilue/leecode/hot100/T86_DecodeString.java new file mode 100644 index 0000000..0b4a6e5 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/hot100/T86_DecodeString.java @@ -0,0 +1,146 @@ +package com.markilue.leecode.hot100; + +import org.junit.Test; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.hot100 + *@Author: markilue + *@CreateTime: 2023-04-06 10:04 + *@Description: + * TODO 力扣394 字符串解码: + * 给定一个经过编码的字符串,返回它解码后的字符串。 + * 编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。 + * 你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。 + * 此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。 + *@Version: 1.0 + */ +public class T86_DecodeString { + + @Test + public void test() { + String s = "3[a]2[bc]"; + System.out.println(decodeString(s)); + } + + @Test + public void test1() { + String s = "3[a2[c]]"; + System.out.println(decodeString(s)); + } + + @Test + public void test2() { + String s = "3[z]2[2[y]pq4[2[jk]e1[f]]]ef"; + System.out.println(decodeString(s)); + } + + int start = 0; + + + public String decodeString(String s) { + return decodeSubString(s.toCharArray(), 1); + } + + + public String decodeSubString(char[] chars, int count) { + if (chars[start] == '[') start++; + + StringBuilder sb = new StringBuilder(); + int sum = 0; + while (start < chars.length && Character.isDigit(chars[start])) { + sum = sum * 10 + (chars[start] - '0'); + start++; + } + //如果前面没有数字,返回1次 + if (sum == 0) { + while (start < chars.length && chars[start] >= 'a' && chars[start] <= 'z') { + sb.append(chars[start++]); + } + } else { + sb.append(decodeSubString(chars, sum)); + } + +// if (start < chars.length && Character.isDigit(chars[start])) { +// sb.append(decodeSubString(chars, 1)); +// } + + if (start < chars.length && chars[start] == ']') { + start++; + } + if (count > 1) { + String k = sb.toString(); + for (int i = 0; i < count - 1; i++) { + sb.append(k); + } + } + + if (start < chars.length && chars[start] != ']') { + sb.append(decodeSubString(chars, 1)); + } + + return sb.toString(); + + + } + + + String src; + int ptr; + + /** + * 官方递归法: + * 思路和本人一致,但是思路更清晰和简洁 + * 速度击败100% 内存击败76.28% + * @param s + * @return + */ + public String decodeString1(String s) { + src = s; + ptr = 0; + return getString(); + } + + public String getString() { + if (ptr == src.length() || src.charAt(ptr) == ']') { + // String -> EPS + return ""; + } + + char cur = src.charAt(ptr); + int repTime = 1; + StringBuilder ret = new StringBuilder(); + + if (Character.isDigit(cur)) { + // String -> Digits [ String ] String + // 解析 Digits + repTime = getDigits(); + // 过滤左括号 + ++ptr; + // 解析 String + String str = getString(); + // 过滤右括号 + ++ptr; + // 构造字符串 + while (repTime-- > 0) { + ret.append(str); + } + } else if (Character.isLetter(cur)) { + // String -> Char String + // 解析 Char + ret.append(String.valueOf(src.charAt(ptr++))); + } + ret.append(getString()); + + return ret.toString(); + } + + public int getDigits() { + int ret = 0; + while (ptr < src.length() && Character.isDigit(src.charAt(ptr))) { + ret = ret * 10 + src.charAt(ptr++) - '0'; + } + return ret; + } + +} diff --git a/Leecode/src/main/java/com/markilue/leecode/hot100/T87_CalcEquation.java b/Leecode/src/main/java/com/markilue/leecode/hot100/T87_CalcEquation.java new file mode 100644 index 0000000..b120ee0 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/hot100/T87_CalcEquation.java @@ -0,0 +1,186 @@ +package com.markilue.leecode.hot100; + +import java.util.*; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.hot100 + *@Author: markilue + *@CreateTime: 2023-04-06 11:47 + *@Description: + * TODO 力扣399 除法求值: + * 给你一个变量对数组 equations 和一个实数值数组 values 作为已知条件,其中 equations[i] = [Ai, Bi] 和 values[i] 共同表示等式 Ai / Bi = values[i] 。每个 Ai 或 Bi 是一个表示单个变量的字符串。 + * 另有一些以数组 queries 表示的问题,其中 queries[j] = [Cj, Dj] 表示第 j 个问题,请你根据已知条件找出 Cj / Dj = ? 的结果作为答案。 + * 返回 所有问题的答案 。如果存在某个无法确定的答案,则用 -1.0 替代这个答案。如果问题中出现了给定的已知条件中没有出现的字符串,也需要用 -1.0 替代这个答案。 + * 注意:输入总是有效的。你可以假设除法运算中不会出现除数为 0 的情况,且不存在任何矛盾的结果。 + *@Version: 1.0 + */ +public class T87_CalcEquation { + + /** + * 官方题解:并查集解法 + * 速度击败100% 内存击败85.81% 0ms + * @param equations + * @param values + * @param queries + * @return + */ + public double[] calcEquation(List> equations, double[] values, List> queries) { + + int equationSize = equations.size(); + UnionFind unionFind = new UnionFind(2 * equationSize); + //第一步:预处理,将变量的值与id进行映射,是的并查集的底层使用数组实现,方便编码 + HashMap hashMap = new HashMap<>(2 * equationSize); + int id = 0; + for (int i = 0; i < equationSize; i++) { + List equation = equations.get(i); + String var1 = equation.get(0); + String var2 = equation.get(1); + if (!hashMap.containsKey(var1)) { + hashMap.put(var1, id); + id++; + } + if (!hashMap.containsKey(var2)) { + hashMap.put(var2, id); + id++; + } + unionFind.union(hashMap.get(var1), hashMap.get(var2), values[i]); + } + + //第二部:做查询 + int queriesSize = queries.size(); + double[] res = new double[queriesSize]; + for (int i = 0; i < queriesSize; i++) { + String var1 = queries.get(i).get(0); + String var2 = queries.get(i).get(1); + Integer id1 = hashMap.get(var1); + Integer id2 = hashMap.get(var2); + if (id1 == null || id2 == null) { + res[i] = -1.0d; + } else { + res[i] = unionFind.isConnected(id1, id2); + } + } + return res; + + } + + + private class UnionFind { + + private int[] parent; + + private double[] weight; + + public UnionFind(int n) { + this.parent = new int[n]; + this.weight = new double[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + weight[i] = 1.0d; + } + } + + public void union(int x, int y, double value) { + int rootX = find(x); + int rootY = find(y); + if (rootX == rootY) { + return; + } + parent[rootX] = rootY; + weight[rootX] = weight[y] * value / weight[x]; + } + + + /** + * 路径压缩 + * @param x + * @return + */ + private int find(int x) { + if (x != parent[x]) {//存在引用链,至少是第一次遍历到才会不相等 + int origin = parent[x]; + parent[x] = find(parent[x]); + weight[x] *= weight[origin]; + } + return parent[x]; + } + + public double isConnected(int x, int y) { + int rootX = find(x); + int rootY = find(y); + if (rootY == rootX) { + return weight[x] / weight[y]; + } else { + return -1.0d; + } + } + + + } + + + /** + * 官方DFS法:本质上就是把数据加入子集当中,然后如果存在引用链就一定能够被连接上,则可以计算 + * 速度击败54.5% 内存击败29.67% 1ms + * @param equations + * @param values + * @param queries + * @return + */ + public double[] calcEquation1(List> equations, double[] values, List> queries) { + double[] result = new double[queries.size()]; + Map> graph = buildGraph(equations, values); + + for (int i = 0; i < queries.size(); i++) { + //dfs寻找对应的引用 + String start = queries.get(i).get(0); + String end = queries.get(i).get(1); + + if (!graph.containsKey(start) || !graph.containsKey(end)) { + result[i] = -1;//有一个没有就肯定计算不出来 + } else { + Set visited = new HashSet<>(); + result[i] = dfs(graph, start, end, visited); + } + } + + return result; + } + + //构建引用链 + private Map> buildGraph(List> equations, double[] values) { + Map> graph = new HashMap<>(); + + for (int i = 0; i < equations.size(); i++) { + String v1 = equations.get(i).get(0); + String v2 = equations.get(i).get(1); + graph.putIfAbsent(v1, new HashMap()); + graph.get(v1).put(v2, values[i]); + + graph.putIfAbsent(v2, new HashMap()); + graph.get(v2).put(v1, 1.0 / values[i]); + } + + return graph; + } + + private double dfs(Map> graph, String start, String end, Set visited) { + visited.add(start); + Map next = graph.get(start); + for (Map.Entry entry : next.entrySet()) { + if (entry.getKey().equals(end)) { + return entry.getValue(); + } + if (!visited.contains(entry.getKey())) { + double nextValue = dfs(graph, entry.getKey(), end, visited); + if (nextValue > 0) {//即不可能是-1就正常返回 + return entry.getValue() * nextValue; + } + } + } + + return -1; + } + +}