目录
【案例1】
【题目描述】
【思路解析】
【代码实现】
【案例2】
【题目描述】【百度面试原题】
【思路解析】
【代码实现】
【案例3】
【题目描述 插入区间】
【思路解析】
【代码实现】
【案例4】
【题目描述 纯coding问题】
【思路解析】
【代码实现】
【案例5】
【题目描述】
【思路解析】
【代码实现】
【案例6】
【题目描述 一棵树上的最大路径和】
【思路解析】
【代码实现】
建立一个数组,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);
}
}
我们先做两个函数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;
}
}
插入区间 给出一个无重叠的 ,按照区间起始端点排序的区间列表。 在列表中插入一个新的区间,你需要确保列表中的区间仍然 有序且不重叠(如果有必要的话,可以 合并区间)。
找到所有与新增区间重叠的区间,然后将这些区间和新增区间进行合并即可。
判断两个区间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;
}
}
}
给定一个全为正数的数组,然后每个数字代表一次位移的距离,然后位移的顺序为右上左下右......,然后如果某一次位移经历了上一次位移路过的地方,则返回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;
}
}
有一个字符串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;
}
}
你可以从一颗树的任一节点,到其他任一节点(限制为节点只能往下走或者不走)。寻找这些所有路径中,路径和最大的路径。
对于任一节点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);
}
}