diff --git a/src/main/java/g3401_3500/s3441_minimum_cost_good_caption/Solution.java b/src/main/java/g3401_3500/s3441_minimum_cost_good_caption/Solution.java index c7646e137..2c06274ba 100644 --- a/src/main/java/g3401_3500/s3441_minimum_cost_good_caption/Solution.java +++ b/src/main/java/g3401_3500/s3441_minimum_cost_good_caption/Solution.java @@ -1,160 +1,69 @@ package g3401_3500.s3441_minimum_cost_good_caption; -// #Hard #String #Dynamic_Programming #2025_02_04_Time_48_ms_(96.00%)_Space_56.50_MB_(89.33%) +// #Hard #String #Dynamic_Programming #2025_03_13_Time_20_ms_(100.00%)_Space_46.12_MB_(96.53%) -@SuppressWarnings({"java:S107", "java:S6541"}) -public class Solution { - private static final int INF = Integer.MAX_VALUE / 2; +import java.util.Arrays; +public class Solution { public String minCostGoodCaption(String caption) { int n = caption.length(); if (n < 3) { return ""; } - char[] arr = caption.toCharArray(); - int[][] prefixCost = new int[n + 1][26]; - for (int i = 0; i < n; i++) { - int orig = arr[i] - 'a'; - for (int c = 0; c < 26; c++) { - prefixCost[i + 1][c] = prefixCost[i][c] + Math.abs(orig - c); - } - } - int[] dp = new int[n + 1]; - int[] nextIndex = new int[n + 1]; - int[] nextChar = new int[n + 1]; - int[] blockLen = new int[n + 1]; - for (int i = 0; i < n; i++) { - dp[i] = INF; - nextIndex[i] = -1; - nextChar[i] = -1; - blockLen[i] = 0; - } - dp[n] = 0; - for (int i = n - 1; i >= 0; i--) { - for (int l = 3; l <= 5; l++) { - if (i + l <= n) { - int bestLetter = 0; - int bestCost = INF; - for (int c = 0; c < 26; c++) { - int costBlock = prefixCost[i + l][c] - prefixCost[i][c]; - if (costBlock < bestCost) { - bestCost = costBlock; - bestLetter = c; - } - } - int costCandidate = dp[i + l] + bestCost; - if (costCandidate < dp[i]) { - dp[i] = costCandidate; - nextIndex[i] = i + l; - nextChar[i] = bestLetter; - blockLen[i] = l; - } else if (costCandidate == dp[i]) { - int cmp = - compareSolutions( - nextChar[i], - blockLen[i], - nextIndex[i], - bestLetter, - l, - (i + l), - nextIndex, - nextChar, - blockLen, - n); - if (cmp > 0) { - nextIndex[i] = i + l; - nextChar[i] = bestLetter; - blockLen[i] = l; - } - } + byte[] s = caption.getBytes(); + int[] f = new int[n + 1]; + f[n - 1] = f[n - 2] = Integer.MAX_VALUE / 2; + byte[] t = new byte[n + 1]; + byte[] size = new byte[n]; + for (int i = n - 3; i >= 0; i--) { + byte[] sub = Arrays.copyOfRange(s, i, i + 3); + Arrays.sort(sub); + byte a = sub[0]; + byte b = sub[1]; + byte c = sub[2]; + byte s3 = t[i + 3]; + int res = f[i + 3] + (c - a); + int mask = b << 24 | s3 << 16 | s3 << 8 | s3; + size[i] = 3; + if (i + 4 <= n) { + byte[] sub4 = Arrays.copyOfRange(s, i, i + 4); + Arrays.sort(sub4); + byte a4 = sub4[0]; + byte b4 = sub4[1]; + byte c4 = sub4[2]; + byte d4 = sub4[3]; + byte s4 = t[i + 4]; + int res4 = f[i + 4] + (c4 - a4 + d4 - b4); + int mask4 = b4 << 24 | b4 << 16 | s4 << 8 | s4; + if (res4 < res || res4 == res && mask4 < mask) { + res = res4; + mask = mask4; + size[i] = 4; } } - } - if (dp[0] >= INF) { - return ""; - } - StringBuilder builder = new StringBuilder(n); - int idx = 0; - while (idx < n) { - int len = blockLen[idx]; - int c = nextChar[idx]; - for (int k = 0; k < len; k++) { - builder.append((char) ('a' + c)); - } - idx = nextIndex[idx]; - } - return builder.toString(); - } - - private int compareSolutions( - int oldLetter, - int oldLen, - int oldNext, - int newLetter, - int newLen, - int newNext, - int[] nextIndex, - int[] nextChar, - int[] blockLen, - int n) { - int offsetOld = 0; - int offsetNew = 0; - int curOldPos; - int curNewPos; - int letOld = oldLetter; - int letNew = newLetter; - int lenOld = oldLen; - int lenNew = newLen; - int nxtOld = oldNext; - int nxtNew = newNext; - while (true) { - if (letOld != letNew) { - return (letOld < letNew) ? -1 : 1; - } - int remainOld = lenOld - offsetOld; - int remainNew = lenNew - offsetNew; - int step = Math.min(remainOld, remainNew); - offsetOld += step; - offsetNew += step; - if (offsetOld == lenOld && offsetNew == lenNew) { - if (nxtOld == n && nxtNew == n) { - return 0; - } - if (nxtOld == n) { - return -1; + if (i + 5 <= n) { + byte[] sub5 = Arrays.copyOfRange(s, i, i + 5); + Arrays.sort(sub5); + byte a5 = sub5[0]; + byte b5 = sub5[1]; + byte c5 = sub5[2]; + byte d5 = sub5[3]; + byte e5 = sub5[4]; + int res5 = f[i + 5] + (d5 - a5 + e5 - b5); + int mask5 = c5 << 24 | c5 << 16 | c5 << 8 | t[i + 5]; + if (res5 < res || res5 == res && mask5 < mask) { + res = res5; + mask = mask5; + size[i] = 5; } - if (nxtNew == n) { - return 1; - } - curOldPos = nxtOld; - letOld = nextChar[curOldPos]; - lenOld = blockLen[curOldPos]; - nxtOld = nextIndex[curOldPos]; - offsetOld = 0; - curNewPos = nxtNew; - letNew = nextChar[curNewPos]; - lenNew = blockLen[curNewPos]; - nxtNew = nextIndex[curNewPos]; - offsetNew = 0; - } else if (offsetOld == lenOld) { - if (nxtOld == n) { - return -1; - } - curOldPos = nxtOld; - letOld = nextChar[curOldPos]; - lenOld = blockLen[curOldPos]; - nxtOld = nextIndex[curOldPos]; - offsetOld = 0; - } else if (offsetNew == lenNew) { - if (nxtNew == n) { - return 1; - } - curNewPos = nxtNew; - letNew = nextChar[curNewPos]; - lenNew = blockLen[curNewPos]; - nxtNew = nextIndex[curNewPos]; - offsetNew = 0; } + f[i] = res; + t[i] = (byte) (mask >> 24); + } + StringBuilder ans = new StringBuilder(n); + for (int i = 0; i < n; i += size[i]) { + ans.append(String.valueOf((char) t[i]).repeat(Math.max(0, size[i]))); } + return ans.toString(); } }