阿里笔试第二题 最长旋律
题目大概是这样的,小明在学旋律,每段旋律都可以用字符串来表示,并且旋律的每个
字符的ASCALL码递增的
比如以下4段旋律 :
aaa
bcd
bcdef
zzz
现在就是要求,小明能够吧这些旋律拼接起来组成最长的旋律。
比如上述例子输出 11 最长的旋律为 aaabcdefzzz
package com.alibaba.interview; import java.util.*; /** * 阿里笔试第2题,最长旋律 * * @author wuddong * @date 2020-03-20 08:48 */ public class Main { /** * 动态保存全部最大值 */ private int maxLength = 0; /** * 后缀和,减枝的时候用到 */ private int[] lengthLeft; /** * 旋律,由输入满足每个字符串递增 */ private List<String> melodies; /** * memo solver max length */ private int memoMaxLength = 0; public Main(List<String> melodies) { melodies.sort(String::compareTo); this.melodies = melodies; lengthLeft = new int[melodies.size() + 1]; // 进行排序, 时间复杂度O(nlog(n)) melodies.sort(String::compareTo); for (int i = melodies.size() - 1; i >= 0; i--) { lengthLeft[i] = melodies.get(i).length() + lengthLeft[i + 1]; } } private int memoSolver() { return memoSolver(new Integer[melodies.size()], 0); } /** * 自定向下记忆法 * 时间复杂度O(N^2) */ private int memoSolver(Integer[] memo, int start) { if (start == melodies.size() - 1) { memo[start] = melodies.get(start).length(); } if (memo[start] != null) { return memo[start]; } String curString = melodies.get(start); int curLen = curString.length(); for (int i = start + 1; i < melodies.size(); i++) { int next = memoSolver(memo, i); if (curString.charAt(curString.length() - 1) <= melodies.get(i).charAt(0)) { memoMaxLength = Math.max(memoMaxLength, curLen + next); } } memo[start] = memoMaxLength; return memoMaxLength; } /** * 动态规划解决该问题,时间复杂度O(N^2) * 空间复杂度O(N) * * @return maxLengthOfCombineMelodyLength */ private int dynamicSolver() { String[] mel = new String[melodies.size()]; melodies.toArray(mel); int[] dp = new int[mel.length]; for (int j = mel.length - 1; j >= 0; j--) { for (int i = j; i < mel.length; i++) { if (i == j) { dp[j] = mel[j].length(); } else if (mel[j].charAt(mel[j].length() - 1) <= mel[i].charAt(0)) { dp[j] = Math.max(dp[j], mel[j].length() + dp[i]); } } } return dp[0]; } /** * 递归回溯解决问题 * 时间复杂度 2^N * * @return maxLengthOfCombineMelodyLength */ public int solve() { // 减枝法 nextMelody(melodies, 0, 0); dynamicSolver(); // 动态规划 if (dynamicSolver() != maxLength) { throw new RuntimeException("dynamicSolver 和 减枝法 解法不一致"); } // 记忆法 if (memoSolver() != maxLength) { System.out.println(memoMaxLength); throw new RuntimeException("memoSolver 和 减枝法 不一样"); } return maxLength; } private void nextMelody(List<String> melodies, int n, int curLength) { if (n == melodies.size()) { return; } String curMelody = melodies.get(n); curLength += curMelody.length(); maxLength = Math.max(maxLength, curLength); char curMelodyLastChar = curMelody.charAt(curMelody.length() - 1); for (int i = n + 1; i < melodies.size(); i++) { char nextMelodyFirstChar = melodies.get(i).charAt(0); int cmp = curMelodyLastChar - nextMelodyFirstChar; if (cmp <= 0 && curLength + lengthLeft[i] > maxLength) { nextMelody(melodies, i, curLength); } } } /** * 执行入口 */ public static void main(String[] args) { Scanner sc = new Scanner(System.in); List<String> melodies = new ArrayList<>(); int n = sc.nextInt(); for (int i = 0; i <= n; i++) { String input = sc.nextLine(); if (input.length() == 0) { continue; } melodies.add(input); } Main mainSolver = new Main(melodies); System.out.println(mainSolver.solve()); } /** * 临场时想到的方法 解法出错 * 不通过 */ private static int maxLengthOfMelodies(List<String> melodies) { melodies.sort(String::compareTo); StringBuilder sb = new StringBuilder(); melodies.forEach(sb::append); String melodiesConcat = sb.toString(); System.out.println(melodiesConcat); // 最长上升子序列 // 处理相同的 int[] dp = new int[melodiesConcat.length()]; int len = 0; int skip = 0; int last = -1; for (char c : melodiesConcat.toCharArray()) { int cur = c - '0'; if (last == cur) { skip++; continue; } int i = Arrays.binarySearch(dp, 0, len, cur); if (i < 0) { // 新单词 i = -(i + 1); } if (i == len) { len++; } dp[i] = cur; last = cur; } return len + skip; } }
希望上述分享,能够给大家带来一点新的思路,接下来的笔试顺利,早点拿到心仪的offer!
欢迎各位留言讨论~