From 404596db40de643612ad1c3673c84d67f55cef58 Mon Sep 17 00:00:00 2001 From: algorithmzuo Date: Thu, 12 Jan 2023 10:40:35 +0800 Subject: [PATCH] modify code --- .../Code01_SimilarStringGroups.java | 88 +++++++++ .../Code02_MaximumFrequencyStack.java | 54 ++++++ .../Code03_LeastOperatorsToExpressNumber.java | 114 ++++++++++++ ..._MinimumNumberOfMovesToMakePalindrome.java | 116 ++++++++++++ .../Code05_BusStationsMinLevelNumbers.java | 173 ++++++++++++++++++ ...养的大厂算法面试题(正在直播) | 51 ++++++ 6 files changed, 596 insertions(+) create mode 100644 算法周更班/class_2023_01_2_week/Code01_SimilarStringGroups.java create mode 100644 算法周更班/class_2023_01_2_week/Code02_MaximumFrequencyStack.java create mode 100644 算法周更班/class_2023_01_2_week/Code03_LeastOperatorsToExpressNumber.java create mode 100644 算法周更班/class_2023_01_2_week/Code04_MinimumNumberOfMovesToMakePalindrome.java create mode 100644 算法周更班/class_2023_01_2_week/Code05_BusStationsMinLevelNumbers.java diff --git a/算法周更班/class_2023_01_2_week/Code01_SimilarStringGroups.java b/算法周更班/class_2023_01_2_week/Code01_SimilarStringGroups.java new file mode 100644 index 0000000..6e20488 --- /dev/null +++ b/算法周更班/class_2023_01_2_week/Code01_SimilarStringGroups.java @@ -0,0 +1,88 @@ +package class_2023_01_2_week; + +// 如果交换字符串 X 中的两个不同位置的字母,使得它和字符串 Y 相等, +// 那么称 X 和 Y 两个字符串相似。如果这两个字符串本身是相等的,那它们也是相似的。 +// 例如,"tars" 和 "rats" 是相似的 (交换 0 与 2 的位置); +// "rats" 和 "arts" 也是相似的,但是 "star" 不与 "tars","rats",或 "arts" 相似。 +// 总之,它们通过相似性形成了两个关联组:{"tars", "rats", "arts"} 和 {"star"}。 +// 注意,"tars" 和 "arts" 是在同一组中,即使它们并不相似。 +// 形式上,对每个组而言,要确定一个单词在组中,只需要这个词和该组中至少一个单词相似。 +// 给你一个字符串列表 strs。列表中的每个字符串都是 strs 中其它所有字符串的一个字母异位词。 +// 请问 strs 中有多少个相似字符串组? +// 测试链接 : https://leetcode.cn/problems/similar-string-groups/ +public class Code01_SimilarStringGroups { + + public static int numSimilarGroups(String[] strs) { + int n = strs.length; + int m = strs[0].length(); + UnionFind uf = new UnionFind(n); + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + // [i] [j] + if (uf.find(i) != uf.find(j)) { + int diff = 0; + for (int k = 0; k < m && diff < 3; k++) { + if (strs[i].charAt(k) != strs[j].charAt(k)) { + diff++; + } + } + if (diff == 0 || diff == 2) { + uf.union(i, j); + } + } + } + } + return uf.sets; + } + + public static class UnionFind { + public int[] father; + public int[] size; + public int[] help; + public int sets; + + public UnionFind(int n) { + father = new int[n]; + size = new int[n]; + help = new int[n]; + for (int i = 0; i < n; i++) { + father[i] = i; + size[i] = 1; + } + sets = n; + } + + public int find(int i) { + int hi = 0; + while (i != father[i]) { + help[hi++] = i; + i = father[i]; + } + while (hi != 0) { + father[help[--hi]] = i; + } + return i; + } + + public void union(int i, int j) { + int fi = find(i); + int fj = find(j); + if (fi != fj) { + if (size[fi] >= size[fj]) { + father[fj] = fi; + size[fi] += size[fj]; + } else { + father[fi] = fj; + size[fj] += size[fi]; + } + sets--; + } + } + + public int sets() { + return sets; + } + + } + +} diff --git a/算法周更班/class_2023_01_2_week/Code02_MaximumFrequencyStack.java b/算法周更班/class_2023_01_2_week/Code02_MaximumFrequencyStack.java new file mode 100644 index 0000000..548ef63 --- /dev/null +++ b/算法周更班/class_2023_01_2_week/Code02_MaximumFrequencyStack.java @@ -0,0 +1,54 @@ +package class_2023_01_2_week; + +import java.util.ArrayList; +import java.util.HashMap; + +// 设计一个类似堆栈的数据结构,将元素推入堆栈,并从堆栈中弹出出现频率最高的元素。 +// 实现 FreqStack 类: +// FreqStack() 构造一个空的堆栈。 +// void push(int val) 将一个整数 val 压入栈顶。 +// int pop() 删除并返回堆栈中出现频率最高的元素。 +// 如果出现频率最高的元素不只一个,则移除并返回最接近栈顶的元素。 +// 测试链接 : https://leetcode.cn/problems/maximum-frequency-stack/ +public class Code02_MaximumFrequencyStack { + + class FreqStack { + + // 出现的最大次数 + private int topTimes; + // 每层节点 + private HashMap> cntValues = new HashMap<>(); + // 每一个数出现了几次 + private HashMap valueTopTime = new HashMap<>(); + + public void push(int val) { + // 当前数词频+1 + valueTopTime.put(val, valueTopTime.getOrDefault(val, 0) + 1); + // 当前数是什么词频 5 7次 + int curTopTimes = valueTopTime.get(val); + if (!cntValues.containsKey(curTopTimes)) { + cntValues.put(curTopTimes, new ArrayList<>()); + } + ArrayList curTimeValues = cntValues.get(curTopTimes); + curTimeValues.add(val); + topTimes = Math.max(topTimes, curTopTimes); + } + + public int pop() { + // 最大词频的那一层的链表(动态数组) + ArrayList topTimeValues = cntValues.get(topTimes); + int ans = topTimeValues.remove(topTimeValues.size() - 1); + if (topTimeValues.size() == 0) { + cntValues.remove(topTimes--); + } + int times = valueTopTime.get(ans); + if (times == 1) { + valueTopTime.remove(ans); + } else { + valueTopTime.put(ans, times - 1); + } + return ans; + } + } + +} diff --git a/算法周更班/class_2023_01_2_week/Code03_LeastOperatorsToExpressNumber.java b/算法周更班/class_2023_01_2_week/Code03_LeastOperatorsToExpressNumber.java new file mode 100644 index 0000000..27a7fba --- /dev/null +++ b/算法周更班/class_2023_01_2_week/Code03_LeastOperatorsToExpressNumber.java @@ -0,0 +1,114 @@ +package class_2023_01_2_week; + +import java.util.HashMap; + +// 给定一个正整数 x,我们将会写出一个形如 x (op1) x (op2) x (op3) x ... 的表达式 +// 其中每个运算符 op1,op2,… 可以是加、减、乘、除之一 +// 例如,对于 x = 3,我们可以写出表达式 3 * 3 / 3 + 3 - 3,该式的值为3 +// 在写这样的表达式时,我们需要遵守下面的惯例: +// 除运算符(/)返回有理数 +// 任何地方都没有括号 +// 我们使用通常的操作顺序:乘法和除法发生在加法和减法之前 +// 不允许使用一元否定运算符(-)。例如,“x - x” 是一个有效的表达 +// 因为它只使用减法,但是 “-x + x” 不是,因为它使用了否定运算符 +// 我们希望编写一个能使表达式等于给定的目标值 target 且运算符最少的表达式 +// 返回所用运算符的最少数量 +// 测试链接 : https://leetcode.cn/problems/least-operators-to-express-number/ +public class Code03_LeastOperatorsToExpressNumber { + + public static int leastOpsExpressTarget(int x, int target) { + return dp(0, target, x, new HashMap<>()) - 1; + } + + // i : 当前来到了x的i次方 + // target : 认为target只能由x的i次方,或者更高次方来解决,不能使用更低次方! + // 返回在这样的情况下,target最少能由几个运算符搞定! + // (3, 1001231) -> 返回值! + // dp.get(3) -> {1001231 对应的value} + public static int dp(int i, int target, int x, HashMap> dp) { + if (dp.containsKey(i) && dp.get(i).containsKey(target)) { + return dp.get(i).get(target); + } + + int ans = 0; + if (target > 0 && i < 39) { + if (target == 1) { + ans = cost(i); + } else { + // 我们把课上没有讲清楚的地方这里写一下 + // 比如 x = 5, target = 73 + // 首先来到5的0次方,要搞定73 + // 73 % 5 = 3,知道余数是3 + // 这个余数3 需要 / 1,得到3,因为此时的余数只能由若干个5的0次方解决 + // 所以73要么是70 + 3 * 1,解决的 + // 或者73是75 - (5 - 3) * 1,解决的 + // 前者代价是搞定3个5的0次方的代价 + 后续搞定70的代价 + // 后者代价是搞定2个5的0次方的代价 + 后续搞定75的代价 + // 如果选择70 + 3 * 1的路线,3 * 1的代价:3 * 搞定5的0次方的代价 + // 后续是70的代价,继续 + // 此时来到5的1次方,要搞定70 + // 70 % 25 = 20,知道余数是20 + // 这个余数20 需要 / 5,得到4 + // 因为此时的余数只能由若干个5的1次方解决 + // 所以70要么是50 + 4 * 5的1次方,解决的 + // 或者70是75 - (5 - 4) * 5的1次方,解决的 + // 前者代价是搞定4个5的1次方的代价 + 后续搞定50的代价 + // 后者代价是搞定1个5的1次方的代价 + 后续搞定75的代价 + // 如果选择50 + 4 * 5的1次方 的路线,后续是50的代价,继续 + // 此时来到5的2次方,要搞定50 + // 50 % 125 = 50,知道余数是50 + // 这个余数50 需要 / 5的2次方,得到2, + // 因为此时的余数只能由若干个5的2次方解决 + // 所以50要么是0 + 2 * 5的2次方,解决的 + // 或者50是125 - (5 - 2) * 5的2次方,解决的 + // 我们课上讲的是这个思路 + // 上面的思路怎么方便的实现,就是下面的code + // 我们来解释一下,大家可以把下面的过程,和上面的分析过程对比一下 + // 会发现等效 + // 比如,i = 0, x = 5, target = 73 + // 表示当前来到5的0次方,x是5(固定的), target是73 + // 73 % 5 = 3,知道余数是3 + // 所以73要么是70 + 3 * 5的0次方,解决的 + // 或者73是75 - (5 - 3) * 5的0次方,解决的 + // 3 * 5的0次方 代价就是 : mod * cost(0) + // (5 - 3) * 5的0次方 代价就是 : (x - mod) * cost(0) + // 假设我们选择70 + 3 * 5的0次方的路线 + // 73 / 5 = 14,也就是代码中的div,解决70的后续过程就是 + // i = 1, x = 5, target = 14 + // 表示当前来到5的1次方,x是5(固定的), target是14(其实是原来的70,除过5了) + // 14 % 5 = 4,我们得到了4,这和上面的过程一样 + // 所以原来的70要么是50 + 4 * 5的1次方,解决的 + // 或者原来的70是75 - (5 - 4) * 5的1次方,解决的 + // 这里变成: + // 现在的14要么是10(因为除了5,其实代表原来的70) + 4 * 5的1次方 + // 现在的14要么是15(因为除了5,其实代表原来的75) - 1 * 5的1次方 + // 14 / 5 = 2,也就是代码中的div, + // 14已经是除以5之后的结果了,再除以5得到了2, + // 其实这个2,代表了原来的50,因为除了两次5 + // 所以,i = 2, x = 5, target = 2 + // 其实代表来到5的2次方,搞定50的代价 + // 所以,i = 2, x = 5, target = 3 + // 其实代表来到5的2次方,搞定75的代价(因为3同样是2次除以5之后的结果) + // 也就是说,每一步的代价,其实都是算对了的 + // 后续依然如此,但是代码这样处理可以写的非常少 + int div = target / x; + int mod = target % x; + int p1 = mod * cost(i) + dp(i + 1, div, x, dp); + int p2 = (x - mod) * cost(i) + dp(i + 1, div + 1, x, dp); + ans = Math.min(p1, p2); + } + } + if (!dp.containsKey(i)) { + dp.put(i, new HashMap<>()); + } + dp.get(i).put(target, ans); + return ans; + } + + // 得到x的i次方这个数字 + // 需要几个运算符,默认前面再加个'+'或'-' + public static int cost(int i) { + return i == 0 ? 2 : i; + } + +} diff --git a/算法周更班/class_2023_01_2_week/Code04_MinimumNumberOfMovesToMakePalindrome.java b/算法周更班/class_2023_01_2_week/Code04_MinimumNumberOfMovesToMakePalindrome.java new file mode 100644 index 0000000..2846c1e --- /dev/null +++ b/算法周更班/class_2023_01_2_week/Code04_MinimumNumberOfMovesToMakePalindrome.java @@ -0,0 +1,116 @@ +package class_2023_01_2_week; + +import java.util.ArrayList; + +// 给你一个只包含小写英文字母的字符串 s 。 +// 每一次 操作 ,你可以选择 s 中两个 相邻 的字符,并将它们交换。 +// 请你返回将 s 变成回文串的 最少操作次数 。 +// 注意 ,输入数据会确保 s 一定能变成一个回文串。 +// 测试链接 : https://leetcode.cn/problems/minimum-number-of-moves-to-make-palindrome/ +public class Code04_MinimumNumberOfMovesToMakePalindrome { + + public static int minMovesToMakePalindrome(String s) { + int n = s.length(); + ArrayList> indies = new ArrayList<>(); + // a -> 0 -> 含有a的所有位置{...} + // b -> 1 -> 含有b的所有位置{...} + for (int i = 0; i < 26; i++) { + indies.add(new ArrayList<>()); + } + // AABAA... + // 12345 + // A -> 0 : {1 2 4 5} + // B -> 1 : {3... } + for (int i = 0, j = 1; i < n; i++, j++) { + int c = s.charAt(i) - 'a'; + indies.get(c).add(j); + } + // 原始下标 -> 该去往的下标 存在arr中 + int[] arr = new int[n + 1]; + // 建立好indexTree,初始时,下标1~n上认为全是1 + IndexTree it = new IndexTree(n); + for (int i = 0, l = 1; i < n; i++, l++) { + // i -> 拿字符 从下标0开始 + // l -> 从下标1开始 + // arr[l] != 0 + // 当前的l,曾经作为姘头之一的右侧,之前填过了! + if (arr[l] == 0) { + int c = s.charAt(i) - 'a'; + // l......r + int r = indies.get(c).remove(indies.get(c).size() - 1); + if (l == r) { + arr[l] = (1 + n) / 2; + it.add(l, -1); + } else { + // l != r + // l -> 左边的序号! 0...l累加和! + int kth = it.sum(l); + arr[l] = kth; + arr[r] = n - kth + 1; + it.add(r, -1); + } + } + } + return number(arr, new int[n + 1], 1, n); + } + + public static class IndexTree { + + public int[] tree; + public int n; + + public IndexTree(int size) { + tree = new int[size + 1]; + n = size; + for (int i = 1; i <= n; i++) { + add(i, 1); + } + } + + public int sum(int i) { + int ans = 0; + while (i > 0) { + ans += tree[i]; + i -= i & -i; + } + return ans; + } + + public void add(int i, int v) { + while (i < tree.length) { + tree[i] += v; + i += i & -i; + } + } + } + + public static int number(int[] arr, int[] help, int l, int r) { + if (l >= r) { + return 0; + } + int mid = l + ((r - l) >> 1); + return number(arr, help, l, mid) + number(arr, help, mid + 1, r) + merge(arr, help, l, mid, r); + } + + public static int merge(int[] arr, int[] help, int l, int m, int r) { + int i = r; + int p1 = m; + int p2 = r; + int ans = 0; + while (p1 >= l && p2 > m) { + ans += arr[p1] > arr[p2] ? (p2 - m) : 0; + help[i--] = arr[p1] > arr[p2] ? arr[p1--] : arr[p2--]; + } + while (p1 >= l) { + help[i--] = arr[p1--]; + } + while (p2 > m) { + help[i--] = arr[p2--]; + } + for (i = l; i <= r; i++) { + arr[i] = help[i]; + } + return ans; + } + +} diff --git a/算法周更班/class_2023_01_2_week/Code05_BusStationsMinLevelNumbers.java b/算法周更班/class_2023_01_2_week/Code05_BusStationsMinLevelNumbers.java new file mode 100644 index 0000000..f6aa535 --- /dev/null +++ b/算法周更班/class_2023_01_2_week/Code05_BusStationsMinLevelNumbers.java @@ -0,0 +1,173 @@ +package class_2023_01_2_week; + +// 一条单向的铁路线上,火车站编号为1~n +// 每个火车站都有一个级别,最低为 1 级。 +// 现有若干趟车次在这条线路上行驶, +// 每一趟都满足如下要求: +// 如果这趟车次停靠了火车站 x,则始发站、终点站之间所有级别大于等于火车站x的都必须停靠。 +//(注意:起始站和终点站自然也算作事先已知需要停靠的站点) +// 现有 m 趟车次的运行情况(全部满足要求), +// 试推算这n个火车站至少分为几个不同的级别。 +// 测试链接 : https://www.luogu.com.cn/problem/P1983 +// 线段树建边 +// 请同学们务必参考如下代码中关于输入、输出的处理 +// 这是输入输出处理效率很高的写法 +// 提交如下方法,把主类名改成Main,可以直接通过 +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Code05_BusStationsMinLevelNumbers { + + public static final int maxn = 100001; + // 1 500 600 1000 + // stops[1,500,600,1000] + // 停靠车站 + public static int[] stops = new int[maxn]; + // 一段线段树范围的id编号 + // id[rt] = x,rt背后的范围这一段,给它的点编号是x + // rt -> 线段树的某个范围的固有属性,l~r,rt + public static int[] id = new int[maxn << 2]; + + + + // id点是否为单点 + // a 单点 范围 虚拟点? + // 70~90 rt = 60 -> 17 single[17] ? + public static boolean[] single = new boolean[maxn << 3]; + + + // id点的入度 + public static int[] inDegree = new int[maxn << 3]; + // id点拓扑排序统计的最大深度(只算路径上的单点数量) + public static int[] singleDeep = new int[maxn << 3]; + // 链式前向星建图用 + public static int[] head = new int[maxn << 3]; + public static int[] to = new int[maxn << 3]; + public static int[] next = new int[maxn << 3]; + // 拓扑排序用 + public static int[] queue = new int[maxn << 3]; + // n为车站个数、nth为线段树上范围的编号计数、eth为边的计数 + public static int n, nth, eth; + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + while (in.nextToken() != StreamTokenizer.TT_EOF) { + n = (int) in.nval; + in.nextToken(); + int m = (int) in.nval; + nth = 0; + eth = 0; + Arrays.fill(single, 0, (n << 2) + m + 1, false); + Arrays.fill(inDegree, 0, (n << 2) + m + 1, 0); + Arrays.fill(singleDeep, 0, (n << 2) + m + 1, 0); + build(1, n, 1); + for (int i = 0; i < m; i++) { + in.nextToken(); + int k = (int) in.nval; + for (int j = 0; j < k; j++) { + in.nextToken(); + stops[j] = (int) in.nval; + } + int curVirtual = ++nth; + // 虚点向停靠车站连边 + for (int j = 0; j < k; j++) { + vLinkStop(curVirtual, stops[j], 1, n, 1); + } + // 不停靠的连续车站向虚点连边 + for (int j = 1; j < k; j++) { + if (stops[j] > stops[j - 1] + 1) { + rangeLinkV(stops[j - 1] + 1, stops[j] - 1, curVirtual, 1, n, 1); + } + } + } + out.println(topoSort()); + out.flush(); + } + + } + + public static void build(int l, int r, int rt) { + id[rt] = ++nth; + if (l == r) { + single[id[rt]] = true; + } else { + int m = (l + r) / 2; + build(l, m, rt << 1); + build(m + 1, r, rt << 1 | 1); + addEdge(id[rt << 1], id[rt]); + addEdge(id[rt << 1 | 1], id[rt]); + } + } + + public static void rangeLinkV(int L, int R, int vid, int l, int r, int rt) { + if (L <= l && r <= R) { + addEdge(id[rt], vid); + } else { + int m = (l + r) / 2; + if (L <= m) { + rangeLinkV(L, R, vid, l, m, rt << 1); + } + if (R > m) { + rangeLinkV(L, R, vid, m + 1, r, rt << 1 | 1); + } + } + } + + // 17 17~17 + public static void vLinkStop(int vid, int stop, int l, int r, int rt) { + if (l == r) { + addEdge(vid, id[rt]); + } else { + int m = (l + r) / 2; + // 1~100 + // 想去的车站是70 70~70 + // 1~50 51~100 + if (stop <= m) { + vLinkStop(vid, stop, l, m, rt << 1); + } else { + vLinkStop(vid, stop, m + 1, r, rt << 1 | 1); + } + } + } + + public static void addEdge(int fid, int tid) { + inDegree[tid]++; + to[++eth] = tid; + next[eth] = head[fid]; + head[fid] = eth; + } + + public static int topoSort() { + int l = 0; + int r = 0; + for (int i = 1; i <= nth; i++) { + if (inDegree[i] == 0) { + queue[r++] = i; + if (single[i]) { + singleDeep[i] = 1; + } + } + } + int ans = 0; + while (l < r) { + int curNode = queue[l++]; + ans = Math.max(ans, singleDeep[curNode]); + for (int edgeIndex = head[curNode]; edgeIndex != 0; edgeIndex = next[edgeIndex]) { + int child = to[edgeIndex]; + singleDeep[child] = Math.max(singleDeep[child], singleDeep[curNode] + (single[child] ? 1 : 0)); + if (--inDegree[child] == 0) { + queue[r++] = child; + } + } + } + return ans; + } + +} \ No newline at end of file diff --git a/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) b/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) index 9882aea..cd58fc4 100644 --- a/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) +++ b/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) @@ -2808,6 +2808,57 @@ I 意味着增加 +第055节 2023年1月第2周流行算法题目解析 + +如果交换字符串 X 中的两个不同位置的字母,使得它和字符串 Y 相等, +那么称 X 和 Y 两个字符串相似。如果这两个字符串本身是相等的,那它们也是相似的。 +例如,"tars" 和 "rats" 是相似的 (交换 0 与 2 的位置); +"rats" 和 "arts" 也是相似的,但是 "star" 不与 "tars","rats",或 "arts" 相似。 +总之,它们通过相似性形成了两个关联组:{"tars", "rats", "arts"} 和 {"star"}。 +注意,"tars" 和 "arts" 是在同一组中,即使它们并不相似。 +形式上,对每个组而言,要确定一个单词在组中,只需要这个词和该组中至少一个单词相似。 +给你一个字符串列表 strs。列表中的每个字符串都是 strs 中其它所有字符串的一个字母异位词。 +请问 strs 中有多少个相似字符串组? +测试链接 : https://leetcode.cn/problems/similar-string-groups/ + +设计一个类似堆栈的数据结构,将元素推入堆栈,并从堆栈中弹出出现频率最高的元素。 +实现 FreqStack 类: +FreqStack() 构造一个空的堆栈。 +void push(int val) 将一个整数 val 压入栈顶。 +int pop() 删除并返回堆栈中出现频率最高的元素。 +如果出现频率最高的元素不只一个,则移除并返回最接近栈顶的元素。 +测试链接 : https://leetcode.cn/problems/maximum-frequency-stack/ + +给定一个正整数 x,我们将会写出一个形如 x (op1) x (op2) x (op3) x ... 的表达式 +其中每个运算符 op1,op2,… 可以是加、减、乘、除之一 +例如,对于 x = 3,我们可以写出表达式 3 * 3 / 3 + 3 - 3,该式的值为3 +在写这样的表达式时,我们需要遵守下面的惯例: +除运算符(/)返回有理数 +任何地方都没有括号 +我们使用通常的操作顺序:乘法和除法发生在加法和减法之前 +不允许使用一元否定运算符(-)。例如,“x - x” 是一个有效的表达 +因为它只使用减法,但是 “-x + x” 不是,因为它使用了否定运算符 +我们希望编写一个能使表达式等于给定的目标值 target 且运算符最少的表达式 +返回所用运算符的最少数量 +测试链接 : https://leetcode.cn/problems/least-operators-to-express-number/ + +给你一个只包含小写英文字母的字符串 s 。 +每一次 操作 ,你可以选择 s 中两个 相邻 的字符,并将它们交换。 +请你返回将 s 变成回文串的 最少操作次数 。 +注意 ,输入数据会确保 s 一定能变成一个回文串。 +测试链接 : https://leetcode.cn/problems/minimum-number-of-moves-to-make-palindrome/ + +一条单向的铁路线上,火车站编号为1~n +每个火车站都有一个级别,最低为 1 级。 +现有若干趟车次在这条线路上行驶, +每一趟都满足如下要求: +如果这趟车次停靠了火车站 x,则始发站、终点站之间所有级别大于等于火车站x的都必须停靠。 +(注意:起始站和终点站自然也算作事先已知需要停靠的站点) +现有 m 趟车次的运行情况(全部满足要求), +试推算这n个火车站至少分为几个不同的级别。 +测试链接 : https://www.luogu.com.cn/problem/P1983 + +