2021.6.18每日一题
对于给定的整数 n, 如果n的k(k>=2)进制数的所有数位全为1,则称 k(k>=2)是 n 的一个好进制。
以字符串的形式给出 n, 以字符串的形式返回 n 的最小好进制。
示例 1:
输入:"13"
输出:"3"
解释:13 的 3 进制是 111。
示例 2:
输入:"4681"
输出:"8"
解释:4681 的 8 进制是 11111。
示例 3:
输入:"1000000000000000000"
输出:"999999999999999999"
解释:1000000000000000000 的 999999999999999999 进制是 11。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/smallest-good-base
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
看了标签,是二分,但是还是没想到怎么二分,遍历的话肯定会超时
官解的方法,可能前面的等比数列还能想到,但是后面的放缩确实不太好想
假设正整数 n在k (k≥2) 进制下的所有数位都为 1,且位数为 m + 1,那么有:
n = k ^ 0 + k ^ 1 + … + k ^ m
当m == 1 时,n = (11)(k进制下),肯定符合要求
可以看到这是一个等比数列,根据等比数列的求和公式,可以有以下结果
这里确定了m的范围
接下来确定k进行放缩,主要是用了二项式定理,可以直接确定k的一个不超过1的大小范围
也看了各种写二分的方法,但是多是python写的,因为二分主要是用在第二步,如果没有放缩的话,k需要二分来求,因为k可能会很大,检查函数中得到的结果可能会更大,python数可以取到无限大,但是java中二分会超出数据范围。
class Solution {
//官解的方法
public String smallestGoodBase(String n) {
long nVal = Long.parseLong(n); //转换成long
//假设正整数 n 在 k(k≥2) 进制下的所有数位都为 1,且位数为 m + 1,那么有:
//由等比数列的前n项和公式,求出m的最大值
int mMax = (int) Math.floor(Math.log(nVal) / Math.log(2));
//m由大到小遍历
for (int m = mMax; m > 1; m--) {
//根据二项式定理确定k的范围
//求出此时对应的k(k可能是整数或者小数)
int k = (int) Math.pow(nVal, 1.0 / m);
long mul = 1, sum = 1;
//计算当前进制下,这个数应该是多少
for (int i = 0; i < m; i++) {
mul *= k;
sum += mul;
}
//如果这个数和所给数相等,就返回这个进制
if (sum == nVal) {
return Integer.toString(k);
}
}
//否则返回最大的进制数n - 1
return Long.toString(nVal - 1);
}
}
2021.6.19每日一题
给定一个字符串数组 arr,字符串 s 是将 arr 某一子序列字符串连接所得的字符串,如果 s 中的每一个字符都只出现过一次,那么它就是一个可行解。
请返回所有可行解 s 中最长长度。
示例 1:
输入:arr = ["un","iq","ue"]
输出:4
解释:所有可能的串联组合是 "","un","iq","ue","uniq" 和 "ique",最大长度为 4。
示例 2:
输入:arr = ["cha","r","act","ers"]
输出:6
解释:可能的解答有 "chaers" 和 "acters"。
示例 3:
输入:arr = ["abcdefghijklmnopqrstuvwxyz"]
输出:26
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-length-of-a-concatenated-string-with-unique-characters
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
我的思路呢,就是暴力回溯,遍历所有可行的情况,细节需要好好处理,超一半
class Solution {
List<String> arr;
int max = 0;
public int maxLength(List<String> arr) {
//数据范围不大,可以暴力
int[] count = new int[26];
this.arr = arr;
backtracking(count, 0, 0);
return max;
}
public void backtracking(int[] count, int index, int res){
//如果到最后了
if(index == arr.size()){
max = Math.max(max, res);
return;
}
for(int i = index; i < arr.size(); i++){
String cur = arr.get(i);
//如果当前字符串长度和结果字符串长度之和大于26了,不可行
if(res + cur.length() > 26)
continue;
//用于判断当前字符串是否符合条件
int[] temp = new int[26];
boolean flag = false;
for(int j = 0; j < cur.length(); j++){
char c = cur.charAt(j);
if(count[c - 'a'] == 1 || temp[c - 'a'] == 1){
flag = true;
break;
}
temp[c - 'a']++;
}
//如果不符合,跳过
if(flag)
continue;
//如果能走到这里,说明这个字符串是合适的
max = Math.max(max, res + cur.length());
for(int j = 0; j < cur.length(); j++){
char c = cur.charAt(j);
count[c - 'a']++;
}
backtracking(count, i + 1, res + cur.length());
//回溯
for(int j = 0; j < cur.length(); j++){
char c = cur.charAt(j);
count[c - 'a']--;
}
}
}
}
可以先预处理arr集合,将符合条件的字符串挑选出来,并用二进制的形式表示
class Solution {
int ans = 0;
public int maxLength(List<String> arr) {
List<Integer> masks = new ArrayList<Integer>();
//预处理每个字符串,将符合条件的字符串用二进制表示
for (String s : arr) {
int mask = 0;
for (int i = 0; i < s.length(); ++i) {
int ch = s.charAt(i) - 'a';
if (((mask >> ch) & 1) != 0) { // 若 mask 已有 ch,则说明 s 含有重复字母,无法构成可行解
mask = 0;
break;
}
mask |= 1 << ch; // 将 ch 加入 mask 中
}
if (mask > 0) {
masks.add(mask);
}
}
backtrack(masks, 0, 0);
return ans;
}
public void backtrack(List<Integer> masks, int pos, int mask) {
//如果遍历到最后了,统计结果
if (pos == masks.size()) {
ans = Math.max(ans, Integer.bitCount(mask));
return;
}
//如果当前字符串和结果字符串没有重复的字母,就添加这个字符串
if ((mask & masks.get(pos)) == 0) { // mask 和 masks[pos] 无公共元素
backtrack(masks, pos + 1, mask | masks.get(pos));
}
//否则,跳过这个字符串
backtrack(masks, pos + 1, mask);
}
}
2021.6.21每日一题
一个王国里住着国王、他的孩子们、他的孙子们等等。每一个时间点,这个家庭里有人出生也有人死亡。
这个王国有一个明确规定的皇位继承顺序,第一继承人总是国王自己。我们定义递归函数 Successor(x, curOrder) ,给定一个人 x 和当前的继承顺序,该函数返回 x 的下一继承人。
Successor(x, curOrder):
如果 x 没有孩子或者所有 x 的孩子都在 curOrder 中:
如果 x 是国王,那么返回 null
否则,返回 Successor(x 的父亲, curOrder)
否则,返回 x 不在 curOrder 中最年长的孩子
比方说,假设王国由国王,他的孩子 Alice 和 Bob (Alice 比 Bob 年长)和 Alice 的孩子 Jack 组成。
一开始, curOrder 为 ["king"].
调用 Successor(king, curOrder) ,返回 Alice ,所以我们将 Alice 放入 curOrder 中,得到 ["king", "Alice"] 。
调用 Successor(Alice, curOrder) ,返回 Jack ,所以我们将 Jack 放入 curOrder 中,得到 ["king", "Alice", "Jack"] 。
调用 Successor(Jack, curOrder) ,返回 Bob ,所以我们将 Bob 放入 curOrder 中,得到 ["king", "Alice", "Jack", "Bob"] 。
调用 Successor(Bob, curOrder) ,返回 null 。最终得到继承顺序为 ["king", "Alice", "Jack", "Bob"] 。
通过以上的函数,我们总是能得到一个唯一的继承顺序。
请你实现 ThroneInheritance 类:
ThroneInheritance(string kingName) 初始化一个 ThroneInheritance 类的对象。国王的名字作为构造函数的参数传入。
void birth(string parentName, string childName) 表示 parentName 新拥有了一个名为 childName 的孩子。
void death(string name) 表示名为 name 的人死亡。一个人的死亡不会影响 Successor 函数,也不会影响当前的继承顺序。你可以只将这个人标记为死亡状态。
string[] getInheritanceOrder() 返回 除去 死亡人员的当前继承顺序列表。
示例:
输入:
["ThroneInheritance", "birth", "birth", "birth", "birth", "birth", "birth", "getInheritanceOrder", "death", "getInheritanceOrder"]
[["king"], ["king", "andy"], ["king", "bob"], ["king", "catherine"], ["andy", "matthew"], ["bob", "alex"], ["bob", "asha"], [null], ["bob"], [null]]
输出:
[null, null, null, null, null, null, null, ["king", "andy", "matthew", "bob", "alex", "asha", "catherine"], null, ["king", "andy", "matthew", "alex", "asha", "catherine"]]
解释:
ThroneInheritance t= new ThroneInheritance("king"); // 继承顺序:king
t.birth("king", "andy"); // 继承顺序:king > andy
t.birth("king", "bob"); // 继承顺序:king > andy > bob
t.birth("king", "catherine"); // 继承顺序:king > andy > bob > catherine
t.birth("andy", "matthew"); // 继承顺序:king > andy > matthew > bob > catherine
t.birth("bob", "alex"); // 继承顺序:king > andy > matthew > bob > alex > catherine
t.birth("bob", "asha"); // 继承顺序:king > andy > matthew > bob > alex > asha > catherine
t.getInheritanceOrder(); // 返回 ["king", "andy", "matthew", "bob", "alex", "asha", "catherine"]
t.death("bob"); // 继承顺序:king > andy > matthew > bob(已经去世)> alex > asha > catherine
t.getInheritanceOrder(); // 返回 ["king", "andy", "matthew", "alex", "asha", "catherine"]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/throne-inheritance
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
说实话,看题的时候,真没看懂在说啥,不知道那个Successor函数到底能不能用
然后思路就是创建一个树吧,谁生了孩子,就在那个人对应的节点增加一个子节点,代表自己的孩子
然后死了就把这个节点的一个属性变成其他值,
继承顺序的列表,就是遍历这颗树吧,根左右,应该是前序遍历,对于已经死亡的结点,不进行输出
class ThroneInheritance {
//写一下多叉树的实现,不同于二叉树,多叉树的子节点个数是不确定的
//父节点与子节点的联系
Map<String, List<String>> edges;
String king;
//死亡的节点
Set<String> dead;
public ThroneInheritance(String kingName) {
edges = new HashMap<>();;
king = kingName;
//死亡的节点
dead = new HashSet<>();
}
public void birth(String parentName, String childName) {
//先得到父亲对应的孩子集合
List<String> list = edges.getOrDefault(parentName, new ArrayList<String>());
list.add(childName);
//重新放入edges集合
edges.put(parentName, list);
}
public void death(String name) {
dead.add(name);
}
public List<String> getInheritanceOrder() {
//前序遍历
List<String> res = new ArrayList<>();
preorder(king, res);
return res;
}
public void preorder(String name, List<String> res){
//如果没有死,加入集合;如何死了,也需要统计他的孩子
if(!dead.contains(name))
res.add(name);
List<String> child = edges.getOrDefault(name, new ArrayList<String>());
//挨个遍历子树
for(String s : child){
preorder(s, res);
}
}
}
/**
* Your ThroneInheritance object will be instantiated and called as such:
* ThroneInheritance obj = new ThroneInheritance(kingName);
* obj.birth(parentName,childName);
* obj.death(name);
* List param_3 = obj.getInheritanceOrder();
*/
三叶姐的单向链表,tql
class ThroneInheritance {
//一个节点表示一个人
class Node {
String name;
Node next;
Node last; // 记录作为父亲的最后一个儿子
boolean isDeleted = false;
Node (String _name) {
name = _name;
}
}
Map<String, Node> map = new HashMap<>();
//虚拟头节点和尾节点
Node head = new Node(""), tail = new Node("");
public ThroneInheritance(String name) {
Node root = new Node(name);
root.next = tail;
head.next = root;
map.put(name, root);
}
//出生
public void birth(String pname, String cname) {
//先创建这个节点
Node node = new Node(cname);
//放在哈希表中,方便查找
map.put(cname, node);
//找到父节点
Node p = map.get(pname);
Node tmp = p;
//找到父节点的最后一个孩子的孩子
while (tmp.last != null) tmp = tmp.last;
//把这个孩子插到最后一个孩子的孩子的后面
node.next = tmp.next;
tmp.next = node;
//更改父节点的最后一个孩子
p.last = node;
}
//死亡,直接置为true
public void death(String name) {
Node node = map.get(name);
node.isDeleted = true;
}
//遍历这个链表,就是继承顺序
public List<String> getInheritanceOrder() {
List<String> ans = new ArrayList<>();
Node tmp = head.next;
while (tmp.next != null) {
if (!tmp.isDeleted) ans.add(tmp.name);
tmp = tmp.next;
}
return ans;
}
}
作者:AC_OIer
链接:https://leetcode-cn.com/problems/throne-inheritance/solution/gong-shui-san-xie-shi-yong-dan-xiang-lia-7t65/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
突然看到一个多叉树变成二叉树的题解
怎么变呢,一个树节点P的左子节点表示P的孩子,而P的右子节点表示P的兄弟。
也就是说,对于一个节点,生了一个孩子,如果此时它还没有孩子,那么就插入P的左子结点,如果有了孩子,就插入P的左子节点的右子节点方向(相当于都是好兄弟)
最后继承顺序,还是前序遍历
class ThroneInheritance {
HashMap<String,TreeNode> map = new HashMap<>();
TreeNode root;
public ThroneInheritance(String kingName) {
root = new TreeNode(kingName);
map.put(kingName,root);
}
//先找到父亲节点,如果是第一个孩子,则插入左节点,如果是其他孩子则插入左节点的最右节点
public void birth(String parentName, String childName) {
TreeNode parent = map.get(parentName);
TreeNode child = new TreeNode(childName);
map.put(childName,child);
if(parent.left == null){
parent.left = child;
}else{
TreeNode cur = parent.left;
while(cur.right != null){
cur = cur.right;
}
cur.right = child;
}
}
//找到该节点,标记为死亡
public void death(String name) {
TreeNode people = map.get(name);
people.state = 1;
}
//前序遍历
public List<String> getInheritanceOrder() {
List<String> order = new ArrayList<>();
visit(root,order);
return order;
}
//辅助的继承顺序遍历函数,实际上就是一个前序遍历
private void visit(TreeNode t,List<String> order){
if(t != null){
if (t.state == 0){
order.add(t.name);
}
visit(t.left,order);
visit(t.right,order);
}
}
}
//定义树节点
class TreeNode{
String name;
int state;
TreeNode left;
TreeNode right;
public TreeNode(String name){
this.name = name;
state = 0;
}
}
/**
* Your ThroneInheritance object will be instantiated and called as such:
* ThroneInheritance obj = new ThroneInheritance(kingName);
* obj.birth(parentName,childName);
* obj.death(name);
* List param_3 = obj.getInheritanceOrder();
*/
作者:bob_king_wei
链接:https://leetcode-cn.com/problems/throne-inheritance/solution/duo-cha-shu-bian-er-cha-shu-qian-xu-bian-qp1u/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
做完这个题我突然想到一个问题,皇位继承确实是按这个顺序继承,还是如果太子死了,就是太子的兄弟继承,而不管太子有没有孩子。
最后呢,查了一下,发现还真是按这个题所给的这种逻辑继承,哈哈哈
6.21每日一题
二进制手表顶部有 4 个 LED 代表 小时(0-11),底部的 6 个 LED 代表 分钟(0-59)。每个 LED 代表一个 0 或 1,最低位在右侧。
给你一个整数 turnedOn ,表示当前亮着的 LED 的数量,返回二进制手表可以表示的所有可能时间。你可以 按任意顺序 返回答案。
小时不会以零开头:
例如,"01:00" 是无效的时间,正确的写法应该是 "1:00" 。
分钟必须由两位数组成,可能会以零开头:
例如,"10:2" 是无效的时间,正确的写法应该是 "10:02" 。
示例 1:
输入:turnedOn = 1
输出:["0:01","0:02","0:04","0:08","0:16","0:32","1:00","2:00","4:00","8:00"]
示例 2:
输入:turnedOn = 9
输出:[]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-watch
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
我的思路是分成小时部分和分钟部分,小时部分有 i 位的话,分钟部分就有turnedOn - i 位,然后回溯,枚举所有情况,加入集合中
过了,超50%
class Solution {
public List<String> readBinaryWatch(int turnedOn) {
//就是枚举呗
List<String> res = new ArrayList<>();
if(turnedOn >= 9)
return res;
//在小时里面最多选3个,在分钟里面最多选5个
int hmax = Math.min(turnedOn, 3);
//选i位当做小时
for(int i = 0; i <= hmax; i++){
List<String> h = new ArrayList<>();
List<String> min = new ArrayList<>();
selectHour(i, 0, 0, h);
selectMinute(turnedOn - i, 0, 0, min);
int l = h.size();
while(l-- > 0){
//取出这个删除的数
String hour = h.remove(0);
for(int j = 0; j < min.size(); j++){
h.add(hour + ":" + min.get(j));
}
}
res.addAll(h);
}
return res;
}
//4位里面选count位
public void selectHour(int count, int cur, int index, List<String> h){
if(cur > 11)
return;
if(count == 0){
h.add(String.valueOf(cur));
return;
}
if(index == 4)
return;
for(int i = index; i < 4; i++){
selectHour(count - 1, cur + (1 << i), i + 1, h);
}
}
//6位里面选count位
public void selectMinute(int count, int cur, int index, List<String> min){
if(cur > 59)
return;
if(count == 0){
if(cur <= 9)
min.add("0" + String.valueOf(cur));
else
min.add(String.valueOf(cur));
return;
}
if(index == 6)
return;
for(int i = index; i < 6; i++){
selectMinute(count - 1, cur + (1 << i), i + 1, min);
}
}
}
但是这样写有点费时间,而且有些细节也比较麻烦,简单题应该不至于这样,所以看了一下题解,发现直接枚举时分,或者二进制枚举就可以了,这样代码编写简单点,如果是比赛的话比较节省时间
//枚举时分
class Solution {
public List<String> readBinaryWatch(int turnedOn) {
List<String> ans = new ArrayList<String>();
for (int h = 0; h < 12; ++h) {
for (int m = 0; m < 60; ++m) {
if (Integer.bitCount(h) + Integer.bitCount(m) == turnedOn) {
ans.add(h + ":" + (m < 10 ? "0" : "") + m);
}
}
}
return ans;
}
}
//二进制枚举,总共10位,前4位表示小时,后6位表示分钟
class Solution {
public List<String> readBinaryWatch(int turnedOn) {
List<String> ans = new ArrayList<String>();
for (int i = 0; i < 1024; ++i) {
int h = i >> 6, m = i & 63; // 用位运算取出高 4 位和低 6 位
if (h < 12 && m < 60 && Integer.bitCount(i) == turnedOn) {
ans.add(h + ":" + (m < 10 ? "0" : "") + m);
}
}
return ans;
}
}
三叶姐的打表,第一次见做题用静态代码块的(静态代码块在类加载时执行),学习一下
class Solution {
// 打表逻辑,也可以放到本地做
// 注意使用 static 修饰,确保打表数据只会被生成一次
static Map<Integer, List<String>> map = new HashMap<>();
static {
for (int h = 0; h <= 11; h++) {
for (int m = 0; m <= 59; m++) {
int tot = getCnt(h) + getCnt(m);
List<String> list = map.getOrDefault(tot, new ArrayList<String>());
list.add(h + ":" + (m <= 9 ? "0" + m : m));
map.put(tot, list);
}
}
}
//统计x中1的个数
static int getCnt(int x) {
int ans = 0;
//每次减去最低位的1
for (int i = x; i > 0; i -= lowbit(i)) ans++;
return ans;
}
static int lowbit(int x) {
return x & -x;
}
public List<String> readBinaryWatch(int t) {
return map.getOrDefault(t, new ArrayList<>());
}
}
作者:AC_OIer
链接:https://leetcode-cn.com/problems/binary-watch/solution/gong-shui-san-xie-jian-dan-ti-xue-da-bia-gwn2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。