Skip to content

Instantly share code, notes, and snippets.

@tatsuyax25
Created May 14, 2025 16:30
Show Gist options
  • Save tatsuyax25/d14705bca28069da9e051d3a251fb108 to your computer and use it in GitHub Desktop.
Save tatsuyax25/d14705bca28069da9e051d3a251fb108 to your computer and use it in GitHub Desktop.
You are given a string s consisting of lowercase English letters, an integer t representing the number of transformations to perform, and an array nums of size 26. In one transformation, every character in s is replaced according to the following rul
/**
* Computes the length of the transformed string after t transformations.
* @param {string} s - Input string consisting of lowercase English letters.
* @param {number} t - Number of transformations to apply.
* @param {number[]} nums - Array of size 26, mapping each letter to its transformation size.
* @returns {number} - Length of the resulting string modulo 10^9 + 7.
*/
function lengthAfterTransformations(s, t, nums) {
const MOD = 1000000007n; // Large prime number for modulo operations
const bigT = BigInt(t); // Convert `t` to BigInt to handle large values
// Step 1: Count occurrences of each character in the input string
let count = Array(26).fill(0n); // Array to track letter frequencies
for (const ch of s) {
count[ch.charCodeAt(0) - 97]++; // Convert character to index ('a' = 0, ..., 'z' = 25)
}
// Step 2: Determine the number of bits needed to represent `t`
let bits = 0;
let tmp = bigT;
while (tmp > 0n) {
bits++;
tmp >>= 1n; // Bitwise right shift to count significant bits
}
if (bits === 0) bits = 1; // Ensure at least one transformation step
// Step 3: Create a transformation table (Sparse Power Table)
const spt = Array.from({ length: 26 }, () =>
Array.from({ length: bits }, () => Array(26).fill(0n))
);
// Step 4: Initialize transformation rules for first step
for (let i = 0; i < 26; i++) {
const maxStep = Math.min(26, nums[i]); // Limit step size to 26 (alphabet size)
for (let step = 1; step <= maxStep; step++) {
spt[i][0][(i + step) % 26]++; // Handle wrap-around transformation
}
}
// Step 5: Precompute transformation results for different bitwise steps
for (let b = 1; b < bits; b++) {
for (let i = 0; i < 26; i++) {
const out = spt[i][b]; // Transformation table for current bit
const prev = spt[i][b - 1]; // Previous bit transformations
for (let mid = 0; mid < 26; mid++) {
const ways1 = prev[mid]; // Ways to transform from previous step
if (ways1 === 0n) continue;
const row2 = spt[mid][b - 1]; // Second stage of transformation
for (let k = 0; k < 26; k++) {
out[k] = (out[k] + ways1 * row2[k]) % MOD; // Combine previous results
}
}
}
}
// Step 6: Apply transformations iteratively based on binary decomposition of `t`
let currCount = count; // Tracking current letter counts
for (let b = 0; b < bits; b++) {
if (((bigT >> BigInt(b)) & 1n) === 1n) { // Check if this bit is set in `t`
const next = Array(26).fill(0n); // New letter counts after transformation
for (let i = 0; i < 26; i++) {
const ci = currCount[i]; // Current character count
if (ci === 0n) continue;
const row = spt[i][b]; // Transformation rules for this step
for (let j = 0; j < 26; j++) {
next[j] = (next[j] + ci * row[j]) % MOD; // Update counts
}
}
currCount = next; // Update count for next iteration
}
}
// Step 7: Compute the final length after `t` transformations
let result = 0n;
for (const v of currCount) {
result = (result + v) % MOD; // Sum up final counts with modulo
}
return Number(result); // Convert BigInt back to regular number for output
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment