题目源自:平安科技2020校招技术岗部分编程题汇总
1. 字符串异构同质判定
请编码实现一个命令行工具,判定两个指定的字符串是否异构同质;异构同质的定义为:一个字符串的字符重新排列后,能变成另一个字符串。
输入描述:
以空格字符分隔的两个字符串;输入字符串的合法字符集为[a-zA-Z0-9 ],大小写敏感,无需考虑异常输入场景。
输出描述:
如果判定两个字符串异构同质,则输出true,否则输出false。
示例:
输入:abc cba
输出:true
思路:
- 输入示例两个字符串以空格分隔,故字符串输入在使用next()方法,空格符视为分隔符;
- 输入两个字符串转换成字符数组,并分别使用Arrays类的排序方法,此时若两数组相等则符合异构同质;
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
boolean flag = true; //标志是否符合异构同质
while(scan.hasNext()){
char []c1 = scan.next().toCharArray();
char []c2 = scan.next().toCharArray();
Arrays.sort(c1);
Arrays.sort(c2);
for(int i = 0;i < c1.length;i++){
if(c1[i]!=c2[i]){ //两个排序后的字符数组的元素依次比较
flag = false;
System.out.println("false");
break; //不符合异构同质退出循环
}
}
}
if(flag){ //如果循环体是从break退出,那么flag已经被置false
System.out.println("true");
}
}
}
2. 查找字符串最长公共子串
请编码实现一个命令行工具,找出指定的2个字符串的最长公共子串。
输入描述:
命令行工具接收两个字符串参数。输入字符串的合法字符集为[a-zA-Z0-9],大小写敏感,无需考虑异常输入场景。
输出描述:
所找到的公共子串;如果存在多个等长的公共子串,则请按字母序排序,依次打印出所有公共子串,每行一个。
示例:
输入:
1234567 12893457
输出:
345
输入:
12a34 34b12
输出:
12
34
思路:动态规划
-
依次取得字符串s1和字符串s2的字符,若某两个字符相等,则说明此字符可能作为公共子串的最末一个字符,此时公共子串的长度为s1和s2从此字符往前比较保持相等的个数。利用二维数组dp记录以同时s1的第i个字符和s2的第j个字符作为子串最末字符的长度。显然当两字符不等时长度为0;而相等时长度为s1和s2从此字符往前一个位置字符的长度加1。
-
例如s1=“12a34”,s2=“34b12”,s1在取到’2’时,s2取非’2’的字符都不构成公共子串;当s也取得’2’时,再从此往前比较s1和s2都取到相等的’1’,而dp记录了以’1’为公共子串的最末字符的长度是1,故dp在字符’2’需记为2,即以’2’为公共子串最末字符的长度是1+1=2。
-
找到所有的可能构成的公共子串中长度最大的,记长度为max,公共子串最末字符在s1中的下标为end,由于可能多个公共子串都是最大长度,故end可能是多个。故用key-value的形式来保存max并动态更新下标end的列表。
//对于s1=“12a34”,s2=“34b12”中,构造dp数组:s1字符作为行,s2字符作为列;
//dp[i][j]存放同时以s1中第i个字符和s2中第j个字符
作为公共子串最末一个字符的子串长度
//初始化行或列为0时dp[i][j]=0,即将dp索引i、j对应到s1和s2的第i和第j个位置
col=0 | 3 | 4 | b | 1 | 2 | |
---|---|---|---|---|---|---|
row=0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | |||||
2 | 0 | |||||
a | 0 | |||||
3 | 0 | |||||
4 | 0 |
//当s1的第i个字符和s2的第j个字符相等时,dp[i][j]=dp[i-1][j-1]+1
col=0 | 3 | 4 | b | 1 | 2 | |
---|---|---|---|---|---|---|
row=0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 0 | 1 | 0 |
2 | 0 | 0 | 0 | 0 | 0 | 2 |
a | 0 | 0 | 0 | 0 | 0 | 0 |
3 | 0 | 1 | 0 | 0 | 0 | 0 |
4 | 0 | 0 | 2 | 0 | 0 | 0 |
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
String s1 = scan.next();
String s2 = scan.next();
List<String> res = getRes(s1,s2);
for(String str:res){
System.out.println(str);
}
}
public static List<String> getRes(String s1,String s2){
List<String> res = new ArrayList<>();
int[][] dp = new int [s1.length()+1][s2.length()+1];
int max = 0; //存放最长公共子串长度
Map<Integer,List<Integer>> map = new HashMap<>(); //key存放max,value存放长度为max的公共子串末位字符在s1中的下标end
for(int i=1;i<=s1.length();i++){
for(int j=1;j<=s2.length();j++){
if(s1.charAt(i-1)==s2.charAt(j-1)){
dp[i][j] = dp[i-1][j-1]+1;
if(dp[i][j]>=max){ //当取到的字符对应的新的长度大于或等于临时的max
max = dp[i][j];
if(!map.containsKey(max)){
map.put(max,new ArrayList<>());
}
map.get(max).add(i-1); //动态更新公共子串长度为max的末位字符在s1中的下标end
}
}
}
}
List<Integer> end = map.get(max); //最终max是最长公共子串长度,下标end可能有多个
for(int i:end){
res.add(s1.substring(i-(max-1),i+1)); //从end下标处往前减长度(max-1)即为公共子串首字符
}
Collections.sort(res);
return res;
}
}
3. 分糖果
n 个小朋友坐在一排,每个小朋友拥有 ai 个糖果,现在你要在他们之间转移糖果,使得最后所有小朋友拥有的糖果数都相同,每一次,你只能从一个小朋友身上拿走恰好两个糖果到另一个小朋友上,问最少需要移动多少次可以平分糖果,如果方案不存在输出 -1。
输入描述:
每个输入包含一个测试用例。每个测试用例的第一行包含一个整数n(1 <= n <= 100),接下来的一行包含n个整数ai(1 <= ai <= 100)。
输出描述:
输出一行表示最少需要移动多少次可以平分苹果,如果方案不存在则输出-1。
示例:
输入:
4
7 15 9 5
输出:
3
思路:
-
n个小朋友加一起的糖果总数为sum=a1+a2+…+an个,目标是转移后每个小朋友都有avg=(sum/n)个。假设存在可行的转移糖果的方案,其中m个小朋友初始糖果数ai比avg多,另外n-m个小朋友的ai比avg少,那么需要满足:avg是整数;m个小朋友每个人的(ai-avg)恰好是2的倍数,那么另外n-m个小朋友的每个人的(ai-avg)也会是二的倍数。
-
例如:三个小朋友,初始各有4,6,8个糖果,sum=4+6+8=18,avg=18/3=6,要平分只需从第三个小朋友转移一次(每次拿两颗)到第一个小朋友,显然转移方案存在;而当三个小朋友初始各有6,7,8个糖果,由于每次转移拿两颗,拿一次后糖果数变成8,7,6实际上还是初始情况,故此情况平分方案不存在。
-
计算转移次数:如果方案存在,考虑有m个初始ai多于avg的小朋友,count为这m个(ai-avg)的总和,即要转移给其他n-m个小朋友的总糖果个数,那么转移次数times=count/2。
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int[] a = new int[n];
int sum = 0,avg = 0;
int count = 0,times = 0;
for(int i = 0;i < n;i++){
a[i] = scan.nextInt();
sum+=a[i];
}
if(sum%n==0){ //糖果总数需要是n个小朋友的倍数(avg是整数)
avg = sum/n;
for(int i = 0;i < n;i++){
if(a[i]>avg){ //初始糖果数大于avg的小朋友(假设m个)
count += a[i]-avg; //count = m个大于avg的个数总和
}
}
if(count%2==0){ //count需要是2的倍数(每次转移2个)
times = count/2; //次数=count/2
System.out.println(times);
}else{ //count不是2的倍数,不存在转移方案
System.out.println(-1);
}
}else{ //sum不是n的倍数,不存在转移方案
System.out.println(-1);
}
}
}