左神中级提升班10

目录

【案例1】

【题目描述】

【思路解析】

【代码实现】

【案例2】

【题目描述】【百度面试原题】

 【思路解析】

【代码实现】

【案例3】

【题目描述  插入区间】

【思路解析】

【代码实现】

【案例4】

【题目描述  纯coding问题】

【思路解析】

【代码实现】

【案例5】

【题目描述】

【思路解析】

【代码实现】

【案例6】

【题目描述 一棵树上的最大路径和】

【思路解析】

【代码实现】


【案例1】

【题目描述】

左神中级提升班10_第1张图片

【思路解析】

建立一个数组,arr[i]表示以i位置结尾时,不重复字符子串最远能达到的位置。然后对于i+1位置上的数字来说它的第一个瓶颈就是i位置最远能达到的位置。第二个瓶颈就是上次出现i+1位置上数字的位置。然后这两个位置最近的便是i+1位置最远能达到的位置。

【代码实现】

import java.util.Scanner;

/**
 * @ProjectName: study3
 * @FileName: Ex1
 * @author:HWJ
 * @Data: 2023/9/12 12:41
 */
public class Ex1 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        String express = input.next();
        char[] str = express.toCharArray();
        int[] res = new int[str.length];
        for (int i = 0; i < str.length; i++) {
            res[i] = -1; // 初始话数组.
        }
        for (int i = 1; i < str.length; i++) {
            res[i] = res[i - 1];
            for (int j = i - 1; j > res[i - 1]; j--) {
                if(str[j] == str[i]){
                    res[i] = j;
                    break;
                }
            }
        }
        int max = Integer.MIN_VALUE;
        int maxIndex = -1;
        for (int i = 0; i < str.length; i++) {
            if(i - res[i] > max){
                max = i - res[i];
                maxIndex = res[i];
            }
        }
        String ans = "";
        for (int i = maxIndex + 1; i < maxIndex + 1 + max; i++) {
            ans += str[i];
        }
        System.out.println(ans);
    }
}

【案例2】

【题目描述】【百度面试原题】

 【思路解析】

我们先做两个函数f函数,g函数,f函数表示以len为长度的字符串有多少个,g函数表示以i字符为开始,且长度为len的字符串有多少个。然后我们考虑某一个普遍情况来说明这两个函数的作用。

假设某个字符串为 f m p。

他们的字典序大小一定为 长度为1的个数 + 长度为2的个数 + a-e开头长度为3的个数。(此时可以订下第一个字母f。)+ g-l开头长度为2的个数。(此时可以订下第二个字符m)。 + n-o长度为1的个数(此时订下第三个字符,如果后序没有字符了。则停止,停止后加1)+ 1.

这样就得到了所求字符串的字典序。

【代码实现】

import java.util.Scanner;

/**
 * @ProjectName: study3
 * @FileName: Ex2
 * @author:HWJ
 * @Data: 2023/9/12 14:56
 */
public class Ex2 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        String str = input.next();
        int ans = kth(str);
        System.out.println(ans);
    }

    // g(int i, int len)函数代表以字母i开头时,长度为len的字符串有多少个。
    // i = 1代表a,, i = 26代表z
    public static int g(int i, int len){
        int sum = 0;
        if(len == 1){
            return 1;
        }
        for (int j = i + 1; j <= 26; j++) {
            sum += g(j, len - 1);
        }
        return sum;
    }

    // f(int len) 函数表示长度为len的字符串有多少个。
    public static int f(int len){
        int sum = 0;
        for (int i = 1; i <= 26; i++) {
            sum += g(i, len);
        }
        return sum;
    }

    public static int kth(String s){
        int sum = 0;
        char[] str = s.toCharArray();
        int len = str.length;
        for (int i = 1; i < len; i++) {
            sum += f(i);
        }
        int first = str[0] - 'a' + 1; // 第一个字符
        for (int i = 1; i < first; i++) {
            sum += g(i, len);
        }
        int pre = first;
        for (int i = 1; i < len; i++) {
            int cur = str[i] - 'a' + 1;
            for (int j = pre + 1; j < cur; j++) {
                sum += g(j, len - i);
            }
            pre = cur;
        }
        return sum + 1;
    }

}

【案例3】

【题目描述  插入区间】

插入区间 给出一个无重叠的 ,按照区间起始端点排序的区间列表。 在列表中插入一个新的区间,你需要确保列表中的区间仍然 有序且不重叠(如果有必要的话,可以 合并区间)。

【思路解析】

找到所有与新增区间重叠的区间,然后将这些区间和新增区间进行合并即可。

判断两个区间a,b是否重叠的条件 当两个区间的区域重叠时 可以表示为 a.start <= b.end && b.start <= a.end

【代码实现】

import java.util.ArrayList;
import java.util.List;

/**
 * @ProjectName: study3
 * @FileName: Ex3
 * @author:HWJ
 * @Data: 2023/9/12 15:47
 */
public class Ex3 {
    public static void main(String[] args) {

    }

    public static List insert(List list, Interval newInterval){
        List result = new ArrayList<>();
        int i = 0;
        while (i < list.size() && list.get(i).end < newInterval.start){
            result.add(list.get(i++));
        }
        // 当两个区间的区域重叠时 可以表示为 a.start <= b.end && b.start <= a.end
        while(i < list.size() && list.get(i).start <= newInterval.end){
            newInterval.start = Math.min(newInterval.start, list.get(i).start);
            newInterval.end = Math.max(newInterval.end, list.get(i++).end);
        }
        result.add(newInterval);
        while (i < list.size()){
            result.add(list.get(i++));
        }
        return result;
    }


    public static class Interval{
        public  int start;
        public  int end;

        public Interval(int start, int end) {
            this.start = start;
            this.end = end;
        }
    }
}

【案例4】

【题目描述  纯coding问题】

给定一个全为正数的数组,然后每个数字代表一次位移的距离,然后位移的顺序为右上左下右......,然后如果某一次位移经历了上一次位移路过的地方,则返回true,如果一直没有经历曾经走过的地方,则返回false。

【思路解析】

先在纸上分析所有可能碰撞的情况,然后再将这些规律逻辑化简。

【代码实现】

package IntermediateLift9;

/**
 * @ProjectName: study3
 * @FileName: Ex4
 * @author:HWJ
 * @Data: 2023/9/12 20:43
 */
public class Ex4 {
    public static void main(String[] args) {

    }

    public static boolean isSelfCrossing(int[] arr) {
        if (arr.length < 4) {
            return false;
        }
        if ( (arr[2] <= arr[0] && arr[3] >= arr[1])
        ||
                (arr.length > 4 && ((arr[3] <= arr[1] && arr[4] >= arr[2]) ||
                        (arr[3] == arr[1] && arr[0] + arr[4] >= arr[2])))){
            return true;
        }
        for (int i = 5; i < arr.length; i++) {
            if (arr[i - 1] <= arr[i - 3]
            && ((arr[i] >= arr[i - 2]) || (arr[i - 2] >= arr[i -4]
            && arr[i - 5] + arr[i - 1] >= arr[i * 3] && arr[i - 4] + arr[i] >= arr[i - 2]))){
                return true;
            }
        }
        return false;
    }
}

【案例5】

【题目描述】

有一个字符串str,然后有一个字符串数组,里面存放了多个字符串。数组中的每个字符串都可以多次使用,请问使用这些字符串来拼接字符串str的方法数。

【思路解析】

简单思路为递归。(就是简单的循环遍历,看代码即可懂)

提速思路,将这个数组的所有字符串生产一个前缀树,再前缀树中寻找子串的时候,就可以通过node的end属性来判断是否为数组中的可用子串。如果找到尽头时停止寻找。

【代码实现】

import java.util.Arrays;
import java.util.HashSet;

/**
 * @ProjectName: study3
 * @FileName: Ex5
 * @author:HWJ
 * @Data: 2023/9/12 21:59
 */
public class Ex5 {
    public static void main(String[] args) {
        

    }

    public static int ways1(String str, String[] arr) {
        HashSet strings = new HashSet<>(Arrays.asList(arr));
        return f1(str, 0, strings);

    }

    public static int f1(String str, int index, HashSet set) {
        int ans = 0;
        for (int end = index; end < str.length(); end++) {
            // 这个生成子字符串的行为其实是一个遍历行为,所以花费也为O(str,length())
            String subStr = str.substring(index, end + 1);
            if (set.contains(subStr)) {
                // hashSet 查询时使用的是hash函数确定所寻找数据的哈希值。
                // 如果寻找数据为普通数据结构,则查找花费为O(1)
                // 但是如果寻找数据为String, 则需要算出数据的所有位信息,然后合并为哈希值,花费为O(str.length())
                ans += f1(str, end + 1, set);
            }
        }
        return ans;
    }

    public static class Node {
        public Node[] nexts;
        public boolean end;

        public Node() {
            this.nexts = new Node[26];
            this.end = false;
        }
    }

    public static int ways2(String str, String[] arr) {
        Node head = new Node();
        Node cur = head;
        for (String s : arr) {
            char[] strs = s.toCharArray();
            for (int i = 0; i < strs.length; i++) {
                Node node = new Node();
                if (cur.nexts[strs[i] - 'a'] == null){
                    cur.nexts[strs[i] - 'a'] = node;
                }
                cur = node;
            }
            cur.end = true;
            cur = head;
        }
        return f2(str.toCharArray(), 0, head);

    }

    // 这里使用前缀树的方法加速,在遍历子字符串的同时判断其是否存在。
    public static int f2(char[] str, int index, Node head) {
        int ans = 0;
        Node cur = head;
        for (int end = index; end < str.length; end++) {
            if (cur.nexts[str[end] - 'a'] == null){
                break;
            }
            cur = cur.nexts[str[end] - 'a'];
            if (cur.end){
                ans += f2(str, end + 1, head);
            }
            
        }
        return ans;
    }
}

【案例6】

【题目描述 一棵树上的最大路径和】

你可以从一颗树的任一节点,到其他任一节点(限制为节点只能往下走或者不走)。寻找这些所有路径中,路径和最大的路径。

【思路解析】

对于任一节点node,有两种情况。

(1)最大路径,从node出发。

a. 最大路径为node本身。

b. 最大路径为 node和以node.left出发的最大路径和

c. 最大路径为 node和以node.right出发的最大路径和

(2)最大路径,不从node出发。

a. 最大路径为 不以node.left出发的最大路径

b. 最大路径为 不以node.right出发的最大路径

【代码实现】

/**
 * @ProjectName: study3
 * @FileName: Ex6
 * @author:HWJ
 * @Data: 2023/9/12 23:00
 */
public class Ex6 {
    public static void main(String[] args) {

    }
    
    public static class ReturnData{
        public int maxPath;
        public int haveMaxPath;

        public ReturnData(int maxPath, int haveMaxPath) {
            this.maxPath = maxPath;
            this.haveMaxPath = haveMaxPath;
        }
    }

    public static class Node{
        public int value;
        public Node left;
        public Node right;

        public Node(int value, Node left, Node right) {
            this.value = value;
            this.left = left;
            this.right = right;
        }
    }

    public static int getMax(Node head){
        ReturnData data = getMaxWay(head);
        return Math.max(data.maxPath, data.haveMaxPath);
    }
    
    public static ReturnData getMaxWay(Node node){
        if (node == null){
            return null;
        }
        ReturnData left = getMaxWay(node.left);
        ReturnData right = getMaxWay(node.right);
        // 情况1 最大路径以node出发。
        int haveMaxPath = Integer.MIN_VALUE;
        if (right != null){
            haveMaxPath = Math.max(haveMaxPath, right.haveMaxPath);
        }
        if (left != null){
            haveMaxPath = Math.max(haveMaxPath, left.haveMaxPath);
        }
        haveMaxPath = Math.max(haveMaxPath, node.value);
        
        // 情况2 最大路径不以node出发
        int maxPath = Integer.MIN_VALUE;
        if (right != null){
            maxPath = Math.max(maxPath, right.maxPath);
        }
        if (left != null){
            maxPath = Math.max(maxPath, left.maxPath);
        }
        return new ReturnData(maxPath, haveMaxPath);
    }
}

你可能感兴趣的:(算法)