1、
给出一个布尔表达式的字符串,比如:true or false and false,表达式只包含true,false,and和or,现在要对这个表达式进行布尔求值,计算结果为真时输出true、为假时输出false,不合法的表达时输出error(比如:true true)。表达式求值是注意and 的优先级比 or 要高,比如:true or false and false,等价于 true or (false and false),计算结果是 true。
解析:and比or的优先级大,一旦遇到and就进行计算,遇到or就再等等,直到最后都是or。可以使用栈,如果当前是操作数true false,就看当前栈是否为空,为空则直接进栈,不为空就看栈的peek是and还是or,and就出栈进行计算,or就进栈。如果当前操作数是and or,判断栈的peek是操作数进栈,否则出错。
当所有都压入栈之后,则进行最后的计算,如果栈非空,且peek为and or则出错,否则如果压出是true则直接为true,如果一直到栈空都没有true出现,则为false。
import java.util.Scanner;
import java.util.Stack;
public class Main {
public static void main(String[] args) {
Scanner sc= new Scanner(System.in);
String str=sc.nextLine();
String[] s=str.split(" ");
Stack<String> stack=new Stack<>();
for(int i=0;i<s.length;i++){
if(s[i].equals("true")||s[i].equals("false")){
if(stack.isEmpty()){
stack.push(s[i]);
}else{
String pop=stack.peek();
if(pop.equals("true")||pop.equals("false")){
System.out.println("error");
return ;
}else{
if(pop.equals("or")){
stack.push(pop);
}else{
stack.pop();//and
String pre=stack.pop();
if(s[i].equals("false")||pre.equals("false")){
stack.push("false");
}else{
stack.push("true");
}
}
}
}
}else{//当前是and or
if(stack.isEmpty()){
System.out.println("error");
return ;
}else{
String top=stack.peek();
if(top.equals("true")||top.equals("false")){
stack.push(s[i]);
}else{
System.out.println("error");
return ;
}
}
}
}
if(!stack.isEmpty()&&(stack.peek().equals("or")||stack.peek().equals("and"))){
System.out.println("error");
return ;
}
//当前栈中可能存在很多操作数和or
while(!stack.isEmpty()){
String cur=stack.pop();
//只要栈中有true,结果就为true
if(cur.equals("true")){
System.out.println("true");
break;
}
//如果知道栈空,还没有遇到true,结果就为false
if(stack.isEmpty()) System.out.println("false");
}
}
}
2、
给出两个字符串,分别是模式串P和目标串T,判断模式串和目标串是否匹配,匹配输出 1,不匹配输出 0。模式串中‘?’可以匹配目标串中的任何字符,模式串中的 ’’可以匹配目标串中的任何长度的串,模式串的其它字符必须和目标串的字符匹配。例如P=a?b,T=acb,则P 和 T 匹配。
输入描述:
输入第一行包含一个字符串p, (1 ≤ |p| ≤ 20).输入第二行包含一个字符串t, (1 ≤ |t| ≤ 20).
输出描述:
输出仅包含0和1的整数,0表示p和t不匹配,1表示p和t匹配。
示例1
输入
a?b
ab
输出
0
示例2
输入
ab
ab
输出
1
示例3
输入
a*b
a(cb
输出
1
import java.util.Scanner;
public class Main1Test {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
String p=sc.nextLine();
String s=sc.nextLine();
int m=s.length();
int n=p.length();
boolean[][] dp=new boolean[m+1][n+1];
dp[0][0]=true;
for(int i=1;i<=n;i++){
dp[0][i]=dp[0][i-1]&&p.charAt(i-1)=='*';
}
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(s.charAt(i-1)==p.charAt(j-1)||p.charAt(j-1)=='?'){
dp[i][j]=dp[i-1][j-1];
}
if(p.charAt(j-1)=='*'){
dp[i][j]=dp[i-1][j]||dp[i][j-1];
}
}
}
System.out.println(dp[m][n]?1:0);
}
}
3、
打车派单场景, 假定有N个订单, 待分配给N个司机。每个订单在匹配司机前,会对候选司机进行打分,打分的结果保存在NN的矩阵A, 其中Aij 代表订单i司机j匹配的分值。 假定每个订单只能派给一位司机,司机只能分配到一个订单。求最终的派单结果,使得匹配的订单和司机的分值累加起来最大,并且所有订单得到分配。
输入描述:
第一行包含一个整数N,2≤N≤10。第二行至第N+1行包含NN的矩阵。
输出描述:
输出分值累加结果和匹配列表,结果四舍五入保留小数点后两位(注意如果有多组派单方式得到的结果相同,则有限为编号小的司机分配编号小的订单,比如:司机1得到1号单,司机2得到2号单,就比司机1得到2号单,司机2得到1号单要好)
示例1
输入
3
1.08 1.25 1.5
1.5 1.35 1.75
1.22 1.48 2.5
输出
5.25
1 2
2 1
3 3
说明
第一行代表得到的最大分值累加结果5.25,四舍五入保留两位小数;第二行至第四行代表匹配的结果[i j],其中i按行递增:订单1被派给司机2,订单2被派给司机1,订单3被派给司机3。使得A12+ A21+ A33= 1.25 + 1.5 + 2.5 = 5.25在所有的组合中最大。
解析:使用回溯方法,依次遍历每一组的分值,找出最大值,另外在计算每组分值时,将对应的订单和司机也匹配上。
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main3 {
static double max=-1000;
static boolean[] visited;
static int n;
static double[][] nums;
static List<int[]> relation=new ArrayList<>();
static List<int[]> maxRelation=new ArrayList<>();
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
nums=new double[n][n];
visited=new boolean[n];
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
nums[i][j]=sc.nextDouble();
}
}
helper(0,0);
System.out.println(String.format("%.2f",max));
for(int[] re:maxRelation){
System.out.println((re[0]+1)+" "+(re[1]+1));
}
}
public static void helper(int row,double count){
//每次遍历完一组值时,就计算当前的最大值
if(row==n){
if(count>=max){
if(count==0){
max=count;
List<int[]> list=new ArrayList<>();
for(int i=0;i<n;i++){
int[] temp=new int[2];
temp[0]=i;
temp[1]=i;
list.add(temp);
}
maxRelation=new ArrayList<>(list);
}else{
max=count;
maxRelation.clear();
maxRelation=new ArrayList<>(relation);
}
}
return ;
}
//回溯 递归遍历每组值
for(int i=0;i<n;i++){
if(!visited[i]){
int[] relationArr=new int[2];
relationArr[0]=row;
relationArr[1]=i;
relation.add(relationArr);
visited[i]=true;
helper(row+1,count+nums[row][i]);
relation.remove(relation.size()-1);
visited[i]=false;
}
}
}
}
4、2110年美团外卖火星第3000号配送站点有26名骑手,分别以大写字母A-Z命名,因此可以称呼这些骑手为黄家骑士特工A,黄家骑士特工B…黄家骑士特工Z,某美团黑珍珠餐厅的外卖流水线上会顺序产出一组包裹,美团配送调度引擎已经将包裹分配到骑手,并在包裹上粘贴好骑手名称,如RETTEBTAE代表一组流水线包裹共9个,同时分配给了名字为A B E R T的5名骑手。请在不打乱流水线产出顺序的情况下,把这组包裹划分为尽可能多的片段,同一个骑手只会出现在其中的一个片段,返回一个表示每个包裹片段的长度的列表。
解析:找出当前字母在字符串中最后出现的位置,然后找到最大最后出现的位置,这个位置之前的可以组成一组。
import java.util.ArrayList;
import java.util.Scanner;
public class Main2 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
String str=sc.next();
ArrayList<Integer> list=new ArrayList<>();
int maxIndex=0;
int i=0;
while(i<str.length()){
char c=str.charAt(i);
maxIndex=str.lastIndexOf(c);
int begin=i;
i++;
while(i<=maxIndex){
char t=str.charAt(i);
int index=str.lastIndexOf(t);
if(index>maxIndex) maxIndex=index;
i++;
}
list.add(maxIndex-begin+1);
}
for (int j = 0; j < list.size(); j++) {
System.out.print(list.get(j)+" ");
}
}
}
5、
已知一种新的火星文的单词由英文字母(仅小写字母)组成,但是此火星文中的字母先后顺序未知。给出一组非空的火星文单词,且此组单词已经按火星文字典序进行好了排序(从小到大),请推断出此火星文中的字母先后顺序。
输入描述:
一行文本,为一组按火星文字典序排序好的单词(单词两端无引号),单词之间通过空格隔开
输出描述:
按火星文字母顺序输出出现过的字母,字母之间无其他字符,如果无法确定顺序或者无合理的字母排序可能,请输出"invalid"(无需引号)
示例1
输入
z x
输出
zx
示例2
输入
wrt wrf er ett rftt
输出
wertf
示例3
输入
z x z
输出
invalid
解析:首先是按照字母的顺序逐一加入到set中,然后每一个字符串和其后面的字符串相比,如果两个字母不同且之前没有同时出现,且将其放入到map中(map中保存每个字符和每个字符之间的路线),所有字母形成一个图之后,判断如果有超过一个的字母的入度为0,则说明会存在无法确认的情况,如果产生了闭环,就是说没有入度为0的字母则也会出现无法确认的情况。
找到顺序就是:首先找到入度为0的字母,然后将其与之相连的路都断掉,再次查找入度为0的字母。
import java.util.*;
/**
* 给定按火星字典序排列的单词,求单词中出现过的字符的字典序
* wrt wrf er ett rftt
* x z x
* abc bbc
* hij ikjk kih jkca jkaa jj
* 构造一个字符的有向无环图,再找拓扑序列
*/
public class Main4 {
// 保存图,定义如果字符i在字符j前,那么存在i到j路径,即map[i][j]=1
private static int[][] map = new int[26][26];
// 保存每个节点的入度
private static int[] indegree = new int[26];
// 记录出现过的字符
private static boolean[] flag = new boolean[26];
private static Set<Character> set = new HashSet<>();
// 保存结果
private static List<Character> ans = new ArrayList<>();
public static void main(String[] args) {
// wrt wrf er ett rftt
Scanner sc = new Scanner(System.in);
String[] input = sc.nextLine().split(" ");
int maxLen = 0;
// 创建有向图
build(input);
// 找拓扑序列
topology();
if(ans.size() == set.size()) {
for (Character c : ans) {
System.out.print(c);
}
} else {
System.out.println("invalid");
}
}
public static void topology() {
while(ans.size() < set.size()) {
// 是否在一个遍历中找到入度为0的节点,如果没找到,要break;如果一次遍历找到两个入度为0的点,说明是无法确认顺序的
int cnt = 0;
for(int i = 0; i < indegree.length; i++) {
if(indegree[i] == 0 && flag[i]) cnt++;
}
if (cnt != 1) break;
// 找没有入度的节点,加入序列,在有向图中删除这个节点和从这个节点出发的边
for (int i = 0; i < indegree.length; i++) {
if (indegree[i] == 0 && flag[i]) {
ans.add((char) ('a' + i));
flag[i] = false; // 删除节点
for (int j = 0; j < 26; j++) { // 删除从这个节点出发的边
if (map[i][j] == 0) continue;
indegree[j]--;
map[i][j] = 0;
}
}
}
}
}
//[wrt wrf er ett rftt]
public static void build(String[] strs) {
// 找到相邻的两个字符串,第一个不相同的字符可以确定字符的顺序
// 如wrt和wrf可以得到:t->f
String pre = strs[0];
for (char c : pre.toCharArray()) {
set.add(c);
}
for(int i = 1; i < strs.length; i++) {
String cur = strs[i];
for (char c : cur.toCharArray()) {
set.add(c);
}
for(int j = 0; j < Math.min(pre.length(), cur.length()); j++) {
if(pre.charAt(j) == cur.charAt(j)) continue;
if(map[pre.charAt(j)-'a'][cur.charAt(j)-'a'] == 1) break;
map[pre.charAt(j)-'a'][cur.charAt(j)-'a'] = 1;
indegree[cur.charAt(j)-'a'] += 1;
flag[pre.charAt(j)-'a'] = true;
flag[cur.charAt(j)-'a'] = true;
break;
}
pre = cur;
}
}
}