LeetCode 824. 山羊拉丁文 / 396. 旋转函数 / 587. 安装栅栏(不会,经典凸包问题,学)

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 824. 山羊拉丁文 / 396. 旋转函数 / 587. 安装栅栏(不会,经典凸包问题,学)相关的知识,希望对你有一定的参考价值。

824. 山羊拉丁文

2022.4.21 每日一题

题目描述

给你一个由若干单词组成的句子 sentence ,单词间由空格分隔。每个单词仅由大写和小写英文字母组成。

请你将句子转换为 “山羊拉丁文(Goat Latin)”(一种类似于 猪拉丁文 - Pig Latin 的虚构语言)。山羊拉丁文的规则如下:

  • 如果单词以元音开头(‘a’, ‘e’, ‘i’, ‘o’, ‘u’),在单词后添加"ma"。
    例如,单词 “apple” 变为 “applema” 。
  • 如果单词以辅音字母开头(即,非元音字母),移除第一个字符并将它放到末尾,之后再添加"ma"。
    例如,单词 “goat” 变为 “oatgma” 。
  • 根据单词在句子中的索引,在单词最后添加与索引相同数量的字母’a’,索引从 1 开始。
    例如,在第一个单词后添加 “a” ,在第二个单词后添加 “aa” ,以此类推。

返回将 sentence 转换为山羊拉丁文后的句子。

示例 1:

输入:sentence = “I speak Goat Latin”
输出:“Imaa peaksmaaa oatGmaaaa atinLmaaaaa”

示例 2:

输入:sentence = “The quick brown fox jumped over the lazy dog”
输出:“heTmaa uickqmaaa rownbmaaaa oxfmaaaaa umpedjmaaaaaa overmaaaaaaa hetmaaaaaaaa azylmaaaaaaaaa ogdmaaaaaaaaaa”

提示:

1 <= sentence.length <= 150
sentence 由英文字母和空格组成
sentence 不含前导或尾随空格
sentence 中的所有单词由单个空格分隔

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/goat-latin
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

判断然后模拟就行了

class Solution 
    public String toGoatLatin(String sentence) 
        Set<Character> set = new HashSet<>();
        set.add('a');
        set.add('e');
        set.add('i');
        set.add('o');
        set.add('u');
        set.add('A');
        set.add('E');
        set.add('I');
        set.add('O');
        set.add('U');

        String[] ss = sentence.split(" ");
        for(int i = 0; i < ss.length; i++)
            String s = ss[i];
            if(set.contains(s.charAt(0)))
                s = s + "ma";
                for(int j = 1; j <= i + 1; j++)
                    s = s + "a";
                ss[i] = s;
            else
                s = s.substring(1, s.length()) + s.charAt(0) + "ma";
                for(int j = 1; j <= i + 1; j++)
                    s = s + "a";
                ss[i] = s;
            
        
        String res = "";
        for(String s : ss)
            res = res + s + " ";
        return res.substring(0, res.length() - 1);
    

class Solution:
    def toGoatLatin(self, sentence: str) -> str:
        yuan = 'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U';
        ss = sentence.split()
        res = ''
        for idx, word in enumerate(ss):
            if word[0] not in yuan:
                word = word[1:] + word[0]
            word += 'ma' + (idx + 1) * 'a'
            res += word + ' '
        return res[0:-1]

396. 旋转函数

2022.4.22 每日一题

题目描述

给定一个长度为 n 的整数数组 nums 。

假设 arrk 是数组 nums 顺时针旋转 k 个位置后的数组,我们定义 nums 的 旋转函数 F 为:

F(k) = 0 * arrk[0] + 1 * arrk[1] + … + (n - 1) * arrk[n - 1]
返回 F(0), F(1), …, F(n-1)中的最大值 。

生成的测试用例让答案符合 32 位 整数。

示例 1:

输入: nums = [4,3,2,6]
输出: 26
解释:
F(0) = (0 * 4) + (1 * 3) + (2 * 2) + (3 * 6) = 0 + 3 + 4 + 18 = 25
F(1) = (0 * 6) + (1 * 4) + (2 * 3) + (3 * 2) = 0 + 4 + 6 + 6 = 16
F(2) = (0 * 2) + (1 * 6) + (2 * 4) + (3 * 3) = 0 + 6 + 8 + 9 = 23
F(3) = (0 * 3) + (1 * 2) + (2 * 6) + (3 * 4) = 0 + 2 + 12 + 12 = 26
所以 F(0), F(1), F(2), F(3) 中的最大值是 F(3) = 26 。

示例 2:

输入: nums = [100]
输出: 0

提示:

n == nums.length
1 <= n <= 10^5
-100 <= nums[i] <= 100

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/rotate-function
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

找规律

class Solution 
    public int maxRotateFunction(int[] nums) 
        //首先,因为数据范围很大,所以不能暴力
        //那么自然而然就想到了能运用什么规律,然后再想相邻两个F之间有什么关系
        //例如f(1) = f(0) + sum - nums[l-1] - (l-1)*nums[l - 1]
        //总结一下 f(x) = f(x-1) + sum - l*nums[l - x]

        int sum = 0;
        for(int n : nums)
            sum += n;
        int l = nums.length;
        int f0 = 0;
        for(int i = 0; i < l; i++)
            f0 += i * nums[i];
        
        int max = f0;
        for(int i = 1; i < l; i++)
            f0 = f0 + sum - l * nums[l - i];
            max = Math.max(max, f0);
        
        return max;
      

class Solution:
    def maxRotateFunction(self, nums: List[int]) -> int:
        s = sum(nums)
        f = sum(i * n for i, n in enumerate(nums))
        res = f
        l = len(nums)
        for i in range(1, l):
            f = f + s - l * nums[-i]
            res = max(res, f)
        return res 

587. 安装栅栏

2022.4.23 每日一题

题目描述

在一个二维的花园中,有一些用 (x, y) 坐标表示的树。由于安装费用十分昂贵,你的任务是先用最短的绳子围起所有的树。只有当所有的树都被绳子包围时,花园才能围好栅栏。你需要找到正好位于栅栏边界上的树的坐标。

示例 1:

输入: [[1,1],[2,2],[2,0],[2,4],[3,3],[4,2]]
输出: [[1,1],[2,0],[4,2],[3,3],[2,4]]
解释:

示例 2:

输入: [[1,2],[2,2],[4,2]]
输出: [[1,2],[2,2],[4,2]]
解释:

即使树都在一条直线上,你也需要先用绳子包围它们。

注意:

所有的树应当被围在一起。你不能剪断绳子来包围树或者把树分成一组以上。
输入的整数在 0 到 100 之间。
花园至少有一棵树。
所有树的坐标都是不同的。
输入的点没有顺序。输出顺序也没有要求。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/erect-the-fence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

真·不会做
想了半天,明白题的意思是找最外围的边界,但是怎么找不会
想到一个方法是先将最上下左右的点连接,形成一个图形,然后遍历其他点,如果点在图形内,那么不能添加,如果在图形外,可以添加
然后去百度了怎么判断一个点在图形内,找到一个做射线找几个交点的方法,想了一下,代码太复杂,而且怎么做线什么的都是问题,不可行

只能看题解了,具体看题解:
https://leetcode-cn.com/problems/erect-the-fence/solution/an-zhuang-zha-lan-by-leetcode-solution-75s3/

这里说一下我的简单理解
首先第一个算法,Jarvis算法,就是先确定一个最左边的点,然后依次与其他所有点连线,判断哪条连线在做左边或者最右边
具体怎么判断呢,就是通过向量叉乘的方式,两个向量叉乘,得到的是一个向量。a,b两个向量叉乘的模 |a×b| = |a|*|b|*sin(向量a,b的夹角)
(这个夹角和点乘的夹角有点不同,最大的区别就是点乘是0到180度,指的是两个向量之间的夹角,而叉乘的夹角是0到360度,是指一个向量转动到另一个向量的角度)
所以说通过这个叉乘,可以判断两个向量之间的夹角是否是大于180度的,这样就可以判断一条边是否在另一条边的左边
用这样的方法,从最左边的点开始,找最右边的边,也就是其他点都在这条边的左边
然后以新的起点出发,找新的边,保证其他点在这个边的左边,这样依次找到一个闭环,就是所要的凸包

这里有个问题,就是在所给的这个代码中,加入了visit数组,判断每个点是否被加入到res中,如果加入了,那么就不重复加入;但是这个有必要吗,或者说正确吗,如果存在这种情况的话,当找到一个已经遍历过的点q,那么再从q出发,遍历到的点还是在res中,就形成了循环,跳不出去了;当然,除非这个点就是出发点,只有这一种可能
所以这个算法要是行得通,那么必须保证加入的每一个点都不是重复点,也就是说,从任意一个新的点出发找到的另一个点形成的边都是全新的,这很显然是符合逻辑的,也就是说这个代码中,可以在判断r的时候,就判断是否在visit中出现过,但是这样就会对最后一个点造成影响,或者说加入visit的时候,判断是否是出发点就行(实际改写了一下,不太行,因为同一条线上的点会重复加入)
但是要明白这个道理,就是说不会出现循环的情况

class Solution 
    public int[][] outerTrees(int[][] trees) 
        int n = trees.length;
        if (n < 4) 
            return trees;
        
        
        //找最左下角的点
        int leftMost = 0;
        for (int i = 0; i < n; i++) 
            if (trees[i][0] < trees[leftMost][0] || 
                (trees[i][0] == trees[leftMost][0] && 
                 trees[i][1] < trees[leftMost][1])) 
                leftMost = i;
            
        

        List<int[]> res = new ArrayList<int[]>();
        boolean[] visit = new boolean[n];
        
        //p最开始为最左下方的点
        int p = leftMost;
        do 
            //与随便一个q相连
            int q = (p + 1) % n;
            //遍历其他所有点,如果pq与qr两条边的夹角大于180度,也就是这个叉集小于0,
            //那么说明点r在pq的右边,那么就加q替换成r
            //最后找到最右边的点,保证其他所有点都在pq的左边
            for (int r = 0; r < n; r++) 
                /* 如果 r 在 pq 的右侧,则 q = r */ 
                if (cross(trees[p], trees[q], trees[r]) < 0) 
                    q = r;
                
            

            /* 是否存在点 i, 使得 p 、q 、i 在同一条直线上 */
            for (int i = 0; i < n; i++) 
                if (visit[i] || i == p || i == q) 
                    continue;
                
                //在用一条线上的点,要加入结果集
                if (cross(trees[p], trees[q], trees[i]) == 0) 
                    res.add(trees[i]);
                    visit[i] = true;
                
            
            //如果当前点没有遍历过,那么加入结果集
            if  (!visit[q]) 
                res.add(trees[q]);
                visit[q] = true;
            
            //下一轮从q开始出发继续找点
            p = q;
        //直到围城一个图形,即开始和结尾的点相遇
         while (p != leftMost);
        return res.toArray(new int[][]);
    

    public int cross(int[] p, int[] q, int[] r) 
        return (q[0] - p[0]) * (r[1] - q[1]) - (q[1] - p[1]) * (r[0] - q[0]);
    


作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/erect-the-fence/solution/an-zhuang-zha-lan-by-leetcode-solution-75s3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution 
    public int[][] outerTrees(int[][] trees) 
        int n = trees.length;
        if (n < 4) 
            return trees;
        
        int bottom = 0;
        /* 找到 y 最小的点 bottom*/
        for (int i = 0; i < n; i++) 
            if (trees[i][1] < trees[bottom][1]) 
                bottom = i;
            
        
        swap(trees, bottom, 0);
        /* 以 bottom 原点,按照极坐标的角度大小进行排序 */
        Arrays.sort(trees, 1, n, (a, b) -> 
            int diff = cross(trees[0], a, b);
            if (diff == 0) 
                return distance(trees[0], a) - distance(trees[0], b);
             else 
                return -diff;
            
        );
        /* 对于凸包最后且在同一条直线的元素按照距离从大到小进行排序 */
        int r = n - 1;
        while (r >= 0 && cross(trees[0], trees[n - 1], trees[r]) == 0) 
            r--;
        
        for (int l = r + 1, h = n - 1; l < h; l++, h--) 
            swap(trees, l, h);
        
        Deque<Integer> stack = new ArrayDeque<Integer>();
        stack.push(0);
        stack.push(1);
        for (int i = 2; i < n; i++) 
            int top = stack.pop();
            /* 如果当前元素与栈顶的两个元素构成的向量顺时针旋转,则弹出栈顶元素 */
            while (!stack.isEmpty() && cross(trees[stack.peek()], trees[top], trees[i]) < 0) 
                top = stack.pop();
            
            stack.push(top);
            stack.push(i);
        

        int size = stack.size();
        int[][] res = new int[size][2];
        for (int i = 0; i < size; i++) 
            res[i] = trees[stack.pop()];
        
        return res;
    

    public int cross(int[] p, int[] q, int[] r) 
        return (q[0] - p[0]) * (r[1] - q[1]) - (q[1] - p[1]) * (r[0] - q[0]);
    

    public int distance(int[] p, int[] q) 
        return (p[0] - q[0]) * (p[0] - q[0]) + (p[1] - q[1]) * (p[1] - q[1]);
    

    public void swap(int[][] trees, int i, int j) 
        int temp0 = trees[i][0], temp1 = trees[i][1];
        trees[i][LeetCode 824. Goat Latin (山羊拉丁文)

824. Goat Latin山羊拉丁文

LeetCode-824 划水记录3

LeetCode 山羊拉丁文[模拟 字符串] HERODING的LeetCode之路

算法千题案例每日LeetCode打卡——99.山羊拉丁文

算法千题案例每日LeetCode打卡——99.山羊拉丁文