LeetCode 483. 最小好进制(数学) / 1239. 串联字符串的最大长度 / 1600. 皇位继承顺序(多叉树) / 401. 二进制手表

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 483. 最小好进制(数学) / 1239. 串联字符串的最大长度 / 1600. 皇位继承顺序(多叉树) / 401. 二进制手表相关的知识,希望对你有一定的参考价值。

483. 最小好进制

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);
    }
}

1239. 串联字符串的最大长度

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);
    }
}

1600. 皇位继承顺序

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<String> 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<String> param_3 = obj.getInheritanceOrder();
 */

作者:bob_king_wei
链接:https://leetcode-cn以上是关于LeetCode 483. 最小好进制(数学) / 1239. 串联字符串的最大长度 / 1600. 皇位继承顺序(多叉树) / 401. 二进制手表的主要内容,如果未能解决你的问题,请参考以下文章

2021-6-18

《LeetCode之每日一题》:64.最小好进制

Leetcode题解——算法思想之数学

LeetCode 483. Smallest Good Base

Leetcode刷题100天—191. 位1的个数( 数学)—day92

Leetcode刷题100天—191. 位1的个数( 数学)—day92