Skip to content

Improved task 3441 #1942

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 13, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 53 additions & 144 deletions src/main/java/g3401_3500/s3441_minimum_cost_good_caption/Solution.java
Original file line number Diff line number Diff line change
@@ -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();
}
}