首页
关于
Search
1
2023 年保研经验贴 [计算机-末九Rank50%-入北计清深-北计直博]
1,114 阅读
2
FreeBSD Kernel-编译环境搭建
424 阅读
3
Linux Kernel-THP阅读分析[Hang up]
407 阅读
4
Linux Kernel-编译调试环境搭建
326 阅读
5
Linux Kernel-源码阅读环境搭建
311 阅读
内核
源码分析
阅读笔记
Rust
语法
习题
读书笔记
深入理解计算机系统
论文
技术之外
开发
rcore-arm
登录
Search
yyi
累计撰写
49
篇文章
累计收到
2
条评论
首页
栏目
内核
源码分析
阅读笔记
Rust
语法
习题
读书笔记
深入理解计算机系统
论文
技术之外
开发
rcore-arm
页面
关于
搜索到
49
篇与
的结果
2024-02-20
FreeBSD Arch Handbook阅读笔记 #7 VM
https://docs.freebsd.org/en/books/arch-handbook/vm/7.1 vm_page_tvm_page_t 管理物理内存,每个vm_page_t对应一个物理页面,vm_page_t属于不同双向链表队列。主要包括活动、非活动、缓存或空闲。缓存和空闲的分页队列更加复杂,会根据L1和L2 Cache的大小分成多个队列。申请新页面的时候也会从Cache的角度来申请对齐的页面。页面通过引用计数持有,通过繁忙计数锁定。VM system通过LRU来对页面进行降级。对于Weird类型的页面,不在队列里,通常是载有一个页表。空闲链表里的页面是真正空闲的,而Cache链表里的页面随时可被重用。当进程访问一个不在页表里的页面时,如果页面在页面队列里,页错误就会导致更少的开销。否则只能从外存读入了。pageout守护进程负责:动态调整分页队列、维持各个队列页面合理比例维持脏页面和干净页面的细分(清洗脏页、升降级)VM会尝试在产生合理的页错误量级的前提下帮助决策何时换出和清洗页面。7.2 vm_object_tFreeBSD实现了一个通用内存对象,这个对象的backend可以是:TypesunbackedSwap-backedPhysical device backedFile-backedVM对象可以被遮蔽,具体可以看我的另一篇笔记https://yirannn.com/source_code/vm_sys.html比较常见的应用就是cow一个vm_page_t同时只能和一个vm_object关联。而不同实例的内存共享由遮蔽特性提供7.3 struct buf对于vnode作为back的vm对象,比如file,对脏页信息的维护和vm对脏页信息的维护是相互独立的。在写回脏页时,vm需要在写回前清除dirty标记。另外文件也需要将文件映射到KVM才能进行操作,这种结构被称为文件系统缓冲区,一般是struct buf,文件系统需要对VM进行操作时,把这个vm对象转换为一个buf,然后把buf中的页面映射在KVM里。同理,磁盘IO也是先把vm_object映射到buf,再在buf上发出IO。7.4 vm_map_t, vm_entry_tFreeBSD通过vm_map_t结构把object和虚拟内存范围相关联,页表通过vm_map_t/vm_entry_t/vm_object_t 直接合成。这一段我其实没太读懂,但是我感觉笔者意思是一个物理页不光是和一个vm_object联系,同时也和页表项联系。但是对于同个object的同个页面,对应的vm_page_t都相同7.5 KVM 内存映射FreeBSD用KVM保存内核结构,其中最大的就是7.3中提到的buf。FreeBSD不会把所有物理内存都映射到KVM,主要利用区域分配器管理KVM
2024年02月20日
113 阅读
0 评论
0 点赞
2024-02-18
Rust入门练习-Week9-LC1642/739/150/1074/2402
LC2402. Meeting Rooms III Hard一开始还以为是经典的排序贪心,+魔改,细读之后发现应该不是。感觉就是个模拟,因为时间范围是可枚举的最开始把所有会议按开始时间塞堆里,对于一个时间点: 遍历所有会议室,踢出已经开完会的若所有会议室都满,跳过若堆顶时间小于等于当前时间,且有空会议室,安排之,会议室k的结束时间设置为now + length考虑了下题目条件,完全不必按时间枚举,按第一维排序之后按会议枚举应该就可以,每次要找到最早结束的会议室,还是要用个堆小脑萎缩了,被倒数第二个点卡了十多分钟,最后发现爆intuse std::{cmp::Reverse, collections::BinaryHeap}; impl Solution { pub fn most_booked(n: i32, mut meetings: Vec<Vec<i32>>) -> i32 { let mut visits = vec![0; n as usize]; let mut heap = BinaryHeap::new(); let n = n as usize; meetings.sort_unstable(); for i in 0..n { heap.push(Reverse((0 as i64, i))); } for i in meetings { while let Some(Reverse((time, idx))) = heap.peek() { let (time, idx) = (*time, *idx); if time < i[0] as i64{ heap.pop(); heap.push(Reverse((i[0] as i64, idx))); } else { break; } } if let Some(Reverse((time, idx))) = heap.pop() { let end_time = time + (i[1] - i[0]) as i64; visits[idx] += 1; heap.push(Reverse((end_time, idx))) // println!("meeting {:?} held in {}, to {}", i, idx, end_time); } } visits.iter().enumerate().max_by_key(|x| (x.1, -(x.0 as i32))).unwrap().0 as i32 } }LC1074. Number of Submatrices That Sum to Target Hard虽然是Hard,但是我感觉 N^4 + 矩阵前缀和就能做,常数应该不大的。考虑sumx 是 (0, 0) 到 (x, y) 的前缀和,求 (a, b) 到 (x, y) 的前缀和是sumx + suma-1 - sumx - suma-1怎么说呢。。这还打败96%impl Solution { pub fn num_submatrix_sum_target(matrix: Vec<Vec<i32>>, target: i32) -> i32 { let mut sum = vec![vec![0; matrix[0].len() + 1]; matrix.len() + 1]; for i in 1..=matrix.len() { for j in 1..=matrix[0].len() { sum[i][j] = matrix[i - 1][j - 1] + sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1]; } } let mut res = 0; for i in 0..=matrix.len() { for j in 0..=matrix[0].len() { for k in 0..i { for l in 0..j { if sum[i][j] - sum[k][j] - sum[i][l] + sum[k][l] == target { res += 1; } } } } } res } }LC150. Evaluate Reverse Polish Notation Med给个后缀表达式,算值impl Solution { pub fn eval_rpn(tokens: Vec<String>) -> i32 { let mut stack = vec![]; for i in tokens { let val = i.parse().unwrap_or(-300); if val == -300 { let (rval, lval) = (stack.pop().unwrap(), stack.pop().unwrap()); let res = match i.as_str() { "+" => lval + rval, "-" => lval - rval, "*" => lval * rval, "/" => lval / rval, _ => panic!("Invalid operator"), }; stack.push(res); } else { stack.push(val); } } stack.last().cloned().unwrap() } }LC739. Daily Temperatures MedFind all j for i s.t. temp[j] > temp[i], 感觉是个单调数据结构从右往左,如果有 temp[i] temp[j], 如果 i < j && temp[i] > temp[j] ,那j就一定没用了,所有i之前的答案最长是i,不会到j倒过来想比较不那么顺畅,有个基本的思路之后我们可以考虑正过来。维护一个单调递减的单调栈对于当前扫到的i,如果栈顶低于i,pop栈顶,维护pop的结果,如果高于i,push进去impl Solution { pub fn daily_temperatures(temperatures: Vec<i32>) -> Vec<i32> { use std::collections::VecDeque; let mut res = vec![0; temperatures.len()]; let mut stack = VecDeque::new(); for i in 0..temperatures.len() { while !stack.is_empty() && temperatures[i] > temperatures[*stack.back().unwrap()] { let idx = stack.pop_back().unwrap(); res[idx] = (i - idx) as i32; } stack.push_back(i); } res } }LC1642. Furthest Building You Can Reach Med感觉挺明显是个贪心,我们肯定是要在高度差更大的楼上用梯子,少的地方用砖,只不过不能简单的排序,因为不一定能达到某个位置我的策略是全用砖,并把用砖的数量扔堆里,当砖不够的时候,就从堆顶取出一些砖,然后放上一个梯子死活被卡一个点,leetcode数据太水了,考虑我这个策略是不是有问题假设说我们在之前把梯子都用光了,会不会出现应该把梯子拿到现在这块的情况呢?应该是不会的,因为如果用了梯子,一定是砖不够了,就算把梯子放在新位置,原来也会断。日,被脑瘫GPT3.5骗了,它骗我BinHeap是小根堆这些个if-else不太优雅,合并下同类项use std::collections::BinaryHeap; impl Solution { pub fn furthest_building(heights: Vec<i32>, mut bricks: i32, mut ladders: i32) -> i32 { let mut heap = BinaryHeap::new(); for i in 1..heights.len() { if heights[i] > heights[i-1] { let diff = heights[i] - heights[i-1]; if diff <= bricks { bricks -= diff; heap.push(diff); println!("{} use {} bricks, {} left", i, diff, bricks); } else if let Some(&val) = heap.peek() { if val > diff && ladders > 0 { bricks += heap.pop().unwrap(); bricks -= diff; heap.push(diff); ladders -= 1; // println!("{} use {} bricks + 1 ladder, bring {} {} left", i, diff, val, bricks); } else if ladders > 0 { // println!("diff is {}, {} use 1 ladder, {} left", diff, i, ladders); ladders -= 1; } else { return (i-1) as i32 } } else if ladders > 0 { // println!("diff is {}, {} use 1 ladder, {} left", diff, i, ladders); ladders -= 1; } else { // println!("bricks : {}, ladder {} need {}", bricks, ladders, diff); return (i-1) as i32; } } } (heights.len()-1) as i32 } }
2024年02月18日
75 阅读
0 评论
0 点赞
2024-02-16
Rust入门练习-Week8-LC392/1048/389/1481/799
LC799. Champagne Tower Med看了下数据范围,只能是从行列上来入手了,感觉是个递推impl Solution { pub fn champagne_tower(poured: i32, query_row: i32, query_glass: i32) -> f64 { let mut dp = vec![vec![0.0; 100]; 100]; let (query_row, query_glass) = (query_row as usize, query_glass as usize); dp[0][0] = poured as f64; for i in 0..query_row { for j in 0..=i { if dp[i][j] > 1.0 { let q: f64 = (dp[i][j] - 1.0) / 2.0; dp[i + 1][j] += q; dp[i + 1][j + 1] += q; } } } dp[query_row][query_glass].min(1.0) } }LC1481. Least Number of Unique Integers after K Removals Med贪心,按出现个数排序,从最少的删impl Solution { pub fn find_least_num_of_unique_ints(arr: Vec<i32>, k: i32) -> i32 { use std::collections::HashMap; let cnt = arr.iter().fold(HashMap::new(), |mut m, x| { *m.entry(x).or_insert(0) += 1; m }); let mut cnts = cnt.iter().map(|(_, &v)| v).collect::<Vec<_>>(); cnts.sort_unstable(); let mut k = k; for i in 0..cnts.len() { if k >= cnts[i] { k -= cnts[i]; cnts[i] = 0; } else { cnts[i] -= k; return cnts[i..].len() as i32; } } 0 } }LC389. Find the Difference Ezuse std::collections::HashMap; impl Solution { pub fn find_the_difference(s: String, t: String) -> char { let (mut m1, mut m2) = (HashMap::new(), HashMap::new()); for i in s.bytes() { *m1.entry(i).or_insert(0) += 1; } for i in t.bytes() { *m2.entry(i).or_insert(0) += 1; } for i in m2.keys() { if m1.get(i).unwrap_or(&0) != m2.get(i).unwrap() { return *i as char; } } ' ' } } use std::collections::HashMap; impl Solution { pub fn find_the_difference(s: String, t: String) -> char { let m1 = s.bytes().fold(HashMap::new(), |mut acc, b| { *acc.entry(b).or_insert(0) += 1; acc }); let m2 = t.bytes().fold(HashMap::new(), |mut acc, b| { *acc.entry(b).or_insert(0) += 1; acc }); m2.iter() .find(|&(k, v)| m1.get(k).unwrap_or(&0) != v) .map(|(&k, _)| k as char) .unwrap() } }LC1048. Longest String Chain Med最多1000个词,答案最多16项,考虑dp先按长度排个序,分割一下,wordsi表示第j个长度为i的单词dpi 表示目前长度为i,使用了第j个单词的可达性dpi -> dpi+1 if j -> k最后倒序看dp[i] 是否存在true复杂度N^2乘个常数可过,这常数均摊应该不大的惭愧,这代码基本上都是Copilot写的,我就写了点注释,改了改bugimpl Solution { pub fn longest_str_chain(words: Vec<String>) -> i32 { // a closure to check if a word is a predecessor of another word let is_predecessor = |a: &str, b: &str| -> bool { if a.len() + 1 != b.len() { return false; } let mut i = 0; let mut j = 0; let mut diff = 0; while i < a.len() { if a.chars().nth(i).unwrap() != b.chars().nth(j).unwrap() { diff += 1; if diff > 1 { return false; } j += 1; } else { i += 1; j += 1; } } true }; // separate words by length let mut words_by_length: Vec<Vec<String>> = vec![vec![]; 17]; for word in words.iter() { words_by_length[word.len()].push(word.clone()); } // dp[i][j] is the if length i string can ending with words_by_length[i][j] let mut dp: Vec<Vec<i32>> = vec![vec![1; 1000]; 17]; let mut max_chain = 1; for i in 1..17 { for j in 0..words_by_length[i].len() { for k in 0..i { for l in 0..words_by_length[k].len() { if is_predecessor(&words_by_length[k][l], &words_by_length[i][j]) { dp[i][j] = dp[i][j].max(dp[k][l] + 1); max_chain = max_chain.max(dp[i][j]); } } } } } max_chain } }LC392. Is Subsequence Ezimpl Solution { pub fn is_subsequence(s: String, t: String) -> bool { let (mut p1, mut p2) = (0, 0); let (s, t) = (s.as_bytes(), t.as_bytes()); while p1 < s.len() && p2 < t.len() { if s[p1] == t[p2] { p1 += 1; } p2 += 1; } p1 == s.len() } }
2024年02月16日
86 阅读
0 评论
0 点赞
2024-02-13
部署一个基于VMWare的FreeBSD工作环境
概述在开发FreeBSD Kernel的过程中,发现基于Linux交叉编译的方法实在是过于不便,大量FreeBSD的脚本都是为了BSD自己准备的,因此搭建一个FreeBSD的开发环境,以便未来减少阻力。该环境至少应该能支持:基础的代码浏览与修改功能基础的代码提示、补全与Linter完备的编译环境测试编译产生的内核的虚拟机在这里,编译环境应该是相对好解决的,我们需要自己找到代码提示补全和虚拟机的方案。FreeBSD直接下载Index of /releases/VM-IMAGES/13.2-RELEASE/amd64/Latest/,然后新建虚拟机加载即可如果用vmware,安个tools先pkg install open-vm-toolsGUI换个源先FreeBSD pkg 源使用帮助 ‒ USTC Mirror Help 文档qcow出现的问题vmdk也一样,需要给虚拟磁盘扩容下先https://developer.aliyun.com/article/758140?spm=a2c6h.12873581.0.0.34342784yoZBWHpkg install xorg xfce startxfce4共享文件夹挂载很坑,这里只提供vmware的在 /boot/loader.conf加入fusefs_load="YES"在 /etc/fstab加入.host:/ /root/mnt/hgfs fusefs rw,mountprog=/usr/local/bin/vmhgfs-fuse 0 0安装toolspkg install open-vm-tools mkdir -p /mnt/hgfs rebootreboot之后可以执行kldstat这里应该有个fusefs.ko就对了此时可以看看/mnt/hgfs下有没有内容了(当然前提是Vmware里配了)到此为止,可以通过共享文件夹的方式进行编译了(但是灰常慢)为FreeBSD配置SSH修改/etc/inetd.conf,把ssh tcp前面的注释干掉修改/etc/rc.conf 加入 sshd_enable="YES"修改/etc/ssh/sshd_config把 PasswordAuthentication 的 no改为yes,注释去掉,允许PAM登录【可选的,我没开其实】把 PermitRootLogin no 改为 yes,注释去掉【可选的,我不是生产环境,用root方便】把 PubkeyAuthentication 注释去掉,允许通过key登录,然后自己配个authorized_keys查看虚拟机ip:用passwd改个密码此时就可以ssh上来啦配置sudo安装个sudo、创建个用户pkg install sudo adduser添加到wheel组,为了可以su到rootpw group mod wheel -m yyi设置sudoersvisudo这行注释去掉测试一下即可配置zsh+ohmyzsh我的这些shell基本上都是一样的ohmyzsh+powerlevel10k,不赘述了配置vscode-remote[失败了]Vscode 的 ssh-remote不支持freebsd,但是配置一下并不难启用一下freebsd的linux兼容模式sudo sysrc linux_enable="YES" sudo service linux start sudo pkg install linux_base-c7vscode-remote最后也没成功,因为remote一定要在ssh后面加个命令(即使我在config中已经制定了remote命令)还是选择git同步吧。最后记得打个快照
2024年02月13日
133 阅读
0 评论
0 点赞
2024-02-09
Rust入门练习-Week7-LC1363/2971/4/1/611
LC611. Valid Triangle Number Med找到能组成三角形的边的组合数,N范围1000考虑朴素:直接N^3 枚举其实只要枚举两个边,第三个边的范围就确定了,(a-b, a+b),只需要有个手段统计这个区间内数字数量即可权值线段树?好像用不着,排个序然后二分查找应该就行不过我估计双指针也行,但是确实不是很擅长impl Solution { pub fn triangle_number(mut nums: Vec<i32>) -> i32 { if nums.len() < 3 { return 0; } nums.sort_unstable(); let mut ans = 0; for i in 0..nums.len() - 2 { for j in i+1..nums.len() - 1 { let s = nums[i] + nums[j]; ans += nums[j+1..].partition_point(|&n| n < s) as i32; } } ans } }LC1. Two Sum Ez基础解法枚举N^2 没啥可说的,考虑能不能更快丢hashmap,理论O(N), 好像也没啥可说的。。use std::collections::HashMap; impl Solution { pub fn two_sum(nums: Vec<i32>, target: i32) -> Vec<i32> { let mut map = HashMap::new(); for (i, num) in nums.iter().enumerate() { let x = target - num; if let Some(&j) = map.get(&x) { return vec![j as i32, i as i32]; } map.insert(num, i); } vec![0, 0] } }LC4. Median of Two Sorted Arrays Hard感觉是什么经典题,给俩有序数组,要求 log 求出中位数首先是我估计先合并了再求也能过,因为 OJ 卡不了那么精确估计。考虑怎么 log 做,我猜肯定是在两个数组上分别二分,然后逐渐逼近答案,这个是时间复杂度限制的,基本不会用别的算法了考虑第一次二分会有什么性质假设数组 a 的中位数是 x, b 的是 y,以 y > x 为例有 >= len/2 的数 > x, 有 >= len/2的数 < y,所以答案一定在[x, y] 范围内。我们好像一下就缩小了很大的范围,两个数组都只剩一半的数了(其实应该更少,但是我们在数组 A 中截取后一半,数组 B 中截取前一半)(x > y 同理)但是问题是这么缩,剩下的数组的中位数好像就偏移了。想解决这个问题其实蛮简单,我不删那么多就好了,我按少的删。比如 A 的一半是 10,B 的一半是 8,我也只删 A 的前八个、B 的后八个(不是真的删,只是范围指针变化了)在这种情况下,一定是中位数的两边各删了 8 个在最后,一定会有较小的数组剩 1 或 2,这时候直接插进去求中位数即可,这种方法目测是 O(log(min(m+n)))的,比题目要求更优秀,不过实现起来比较麻烦。还有一个小细节,插入的复杂度是 O(N)的,但是我们这里只会插1-2 个数,插到大数组里不会让大数组的中位数偏移超过 1(极端情况,数组 1 [1, 2, 3, 4, 5, 6, 7, 8],数组 2[9, 10]),所以只留大数组中间四个数即可看了下官方题解,还不如我这个好理解,写一下验证一下。use std::mem::swap; impl Solution { fn mid_segment<'a>(nums: &'a[i32]) -> &'a[i32] { match nums.len() { 0..=4 => nums, _ => { let lower = ((nums.len() - 1) as f64 / 2.0).floor() as usize - 1; let upper = ((nums.len() - 1) as f64 / 2.0).ceil() as usize + 2; &nums[lower..upper] } } } pub fn find_median_sorted_arrays(nums1: Vec<i32>, nums2: Vec<i32>) -> f64 { let (mut n1, mut n2) = (&nums1[..], &nums2[..]); let med = |arr: &[i32]| { let mid = (arr.len() - 1) as f64 / 2.0; let (left, right) = (mid.floor() as usize, mid.ceil() as usize); ((arr[left] + arr[right]) as f64 / 2.0, mid) }; while n1.len() > 2 && n2.len() > 2 { let (mut m1, mut m2) = (med(n1), med(n2)); if m1.0 > m2.0 { swap(&mut n1, &mut n2); swap(&mut m1, &mut m2); } let remove = std::cmp::min(m1.1.floor() as usize, n2.len() - m2.1.ceil() as usize - 1); n1 = &n1[remove..]; n2 = &n2[..(n2.len()-remove)]; } let mut res = Self::mid_segment(n1).to_vec(); res.extend(Self::mid_segment(n2)); res.sort(); med(&res).0 } }LC2971. Find Polygon With the Largest Perimeter Med感觉像排序+前缀和,然后从后往前扫到第一个 sum[i] > num[i+1] 的impl Solution { pub fn largest_perimeter(mut nums: Vec<i32>) -> i64 { nums.sort_unstable(); let sum: Vec<_> = nums .iter() .scan(0, |acc, &num| { *acc += num as i64; Some(*acc) }) .collect(); sum.iter() .enumerate() .rev() .skip(1) .find(|&(i, &v)| v > nums[i + 1] as i64) .map_or(-1, |(i, _)| sum[i] + nums[i + 1] as i64) } }LC1363. Largest Multiple of Three Hard给定一些数码,拼一个最大的能被三整除的数首先明显有俩性质,不知道老外知不知道,但是中国学生肯定都知道数字越长肯定越大数字加和能被 3 整除,不管咋排列都能被 3 整除所以这个问题就变成了找到最多的数使其加和能被 3 整除,在此基础上对数码们排个序先 DP 再贪心,简单的 dp 方案很显然,dpi 表示选了 i 及 i 之前的数,且余数为 k(0, 1, 2)的前提下,最多能选几个数初始 dpi % 3] = 1,其他为 -1(不可达)问题是这么 dp 不好找方案,还得维护个数组记录当前这个数从哪来的遇到了几个坑前导零,这个预判到了,但是方案错了,一开始先 parse 再转回来,但是稍微大点的数这个方案就不能用了多方案,比如会出现用 2 替换 8 的情况(因为%3 都相等),要在 dp 的时候特判。因为是完全等价的,所以这块可以贪心的看当前的数码就行。这个死活调不出来,不知道哪里出问题了,最后选择对数码排个序N^2卡的很紧,因为数据范围是10^4 我本来以为常数小,但是被卡 T 了一个点,在这个时候我去看正解是 nlog的,非常伤心。但是我相信我能卡进去!第一步是做一点优化,发现 0、3、6、9只要有解就要加进去, 所以我们先把 0、3、6 抽出来,最后统一加,加了这个优化的结果是被一个没有0369的卡了第二步是初始化 vec 的大小第三步是大无语事件发生了,我把算法丢给gpt,想让它帮我卡常,结果他直接丢了个正解给我,显得我很呆算了,最后也没卡过去,但是过了绝大多数的点, 没过去的点直接run都卡到400多ms了,不知道为什么submit就过不去impl Solution { pub fn largest_multiple_of_three(digits: Vec<i32>) -> String { let threes: Vec<i32> = digits.iter().cloned().filter(|&x| x % 3 == 0).collect(); let mut digits: Vec<i32> = digits.into_iter().filter(|&x| x % 3 != 0).collect(); digits.sort_unstable_by_key(|a| -a); let n = digits.len(); let mut dp = vec![vec![-1; 3]; n+1]; let mut from = vec![vec![(n, 0); 3]; n+1]; for i in 0..n { dp[i][digits[i] as usize % 3] = 1; for j in 0..i { for k in 0..3 { let to = (digits[i] as usize + k) % 3; if dp[j][k] == -1 { continue; } if dp[i][to] < dp[j][k] + 1 { dp[i][to] = dp[j][k] + 1; from[i][to] = (j, k); } } } } let mut maxn = (0..n).max_by_key(|&i| (dp[i][0], digits[i])).unwrap_or(0); if dp[maxn][0] == -1 { if threes.len() == 0 { return "".to_string(); } maxn = n; } let mut res = Vec::with_capacity((dp[maxn][0] + threes.len() as i32) as usize); let mut cur = 0; while maxn < n { let last = from[maxn][cur]; res.push(digits[maxn]); maxn = last.0; cur = last.1; } res.extend(threes); res.sort_unstable_by(|a, b| b.cmp(a)); println!("{:?}", res); if res[0] == 0 { return "0".to_string(); } res .into_iter() .map(|i| i.to_string()) .collect() } }
2024年02月09日
72 阅读
0 评论
0 点赞
1
...
3
4
5
...
10