从字符串转换到矩阵快速幂:解决多次转换后的长度问题

发布于:2025-05-15 ⋅ 阅读:(12) ⋅ 点赞:(0)

引言

在编程竞赛和算法问题中,我们经常会遇到需要对字符串进行多次转换的问题。本文将介绍一个有趣的问题:给定一个字符串和转换规则,计算经过多次转换后字符串的长度。由于直接模拟会导致性能问题,我们将使用矩阵快速幂来高效解决这个问题。

问题描述

给定:

  • 一个由小写字母组成的字符串 s

  • 一个整数 t 表示转换次数

  • 一个长度为26的数组 nums,其中 nums[i] 表示字母 'a'+i 的转换规则

转换规则:

  1. 将 s[i] 替换为字母表中后续的 nums[s[i]-'a'] 个连续字符

  2. 如果超过 'z' 则回绕到字母表开头

要求返回经过 t 次转换后的字符串长度,结果对 10^9+7 取模。

示例分析

假设:

  • s = "a"

  • t = 2

  • nums = [3, 1, 1, ..., 1] (只有第一个元素是3)

第一次转换:

  • 'a' → 'b' + 'c' + 'd' = "bcd" (长度3)

第二次转换:

  • 'b' → 'c' (长度1)

  • 'c' → 'd' (长度1)

  • 'd' → 'e' (长度1)
    总长度 = 1 + 1 + 1 = 3

直接模拟的局限性

直接模拟每次转换的过程对于小的 t 是可行的,但当 t 很大时(比如 10^9),这种方法会非常低效甚至不可行,因为字符串长度会呈指数级增长。

矩阵快速幂解法

思路概述

  1. 统计字符频率:记录初始字符串中每个字符的出现次数

  2. 构建转换矩阵:表示每个字符如何转换为其他字符

  3. 矩阵快速幂:高效计算转换矩阵的 t 次幂

  4. 计算最终长度:将初始频率向量与转换矩阵相乘,求和得到最终长度

详细步骤

1. 统计字符频率

java

long[] count = new long[26];
for (char c : s.toCharArray()) {
    count[c - 'a']++;
}
2. 构建转换矩阵

对于每个字符 i,计算它转换为其他字符的分布:

java

long[][] matrix = new long[26][26];
for (int i = 0; i < 26; i++) {
    int k = nums[i];
    int q = k / 26;  // 完整循环次数
    int r = k % 26;  // 剩余字符数
    
    if (r == 0) {
        // 均匀分布
        for (int j = 0; j < 26; j++) {
            matrix[i][j] = q % MOD;
        }
    } else {
        // 部分循环
        int start = (i + 1) % 26;
        int end = (i + r) % 26;
        
        for (int j = 0; j < 26; j++) {
            boolean inRange;
            if (start <= end) {
                inRange = (j >= start && j <= end);
            } else {
                inRange = (j >= start || j <= end);
            }
            matrix[i][j] = (q + (inRange ? 1 : 0)) % MOD;
        }
    }
}
3. 矩阵快速幂

java

private long[][] matrixPower(long[][] a, int power) {
    long[][] result = createIdentityMatrix();
    while (power > 0) {
        if ((power & 1) == 1) {
            result = multiplyMatrices(result, a);
        }
        a = multiplyMatrices(a, a);
        power >>= 1;
    }
    return result;
}
4. 计算最终长度

java

long[] finalCount = multiplyVectorMatrix(count, matrixPower);
long total = 0;
for (long num : finalCount) {
    total = (total + num) % MOD;
}
return (int) total;

复杂度分析

  • 时间复杂度:O(26^3 * log t)

    • 矩阵乘法:O(26^3)

    • 快速幂:O(log t)

  • 空间复杂度:O(26^2) 用于存储矩阵

完整代码实现

java

import java.util.List;

class Solution {
    private static final int MOD = 1000000007;
    private static final int SIZE = 26;

    public int lengthAfterTransformations(String s, int t, List<Integer> nums) {
        int[] numsArray = new int[SIZE];
        for (int i = 0; i < SIZE; i++) {
            numsArray[i] = nums.get(i);
        }

        if (t == 0) {
            return s.length() % MOD;
        }

        long[] count = new long[SIZE];
        for (char c : s.toCharArray()) {
            count[c - 'a']++;
        }

        long[][] matrix = buildMatrix(numsArray);
        long[][] matrixPower = matrixPower(matrix, t);
        long[] finalCount = multiplyVectorMatrix(count, matrixPower);

        long total = 0;
        for (long num : finalCount) {
            total = (total + num) % MOD;
        }

        return (int) total;
    }

    private long[][] buildMatrix(int[] nums) {
        long[][] matrix = new long[SIZE][SIZE];
        for (int i = 0; i < SIZE; i++) {
            int k = nums[i];
            int q = k / 26;
            int r = k % 26;

            if (r == 0) {
                for (int j = 0; j < SIZE; j++) {
                    matrix[i][j] = q % MOD;
                }
            } else {
                int start = (i + 1) % 26;
                int end = (i + r) % 26;

                for (int j = 0; j < SIZE; j++) {
                    boolean inRange;
                    if (start <= end) {
                        inRange = (j >= start && j <= end);
                    } else {
                        inRange = (j >= start || j <= end);
                    }

                    matrix[i][j] = (q + (inRange ? 1 : 0)) % MOD;
                }
            }
        }
        return matrix;
    }

    private long[][] matrixPower(long[][] a, int power) {
        long[][] result = createIdentityMatrix();
        while (power > 0) {
            if ((power & 1) == 1) {
                result = multiplyMatrices(result, a);
            }
            a = multiplyMatrices(a, a);
            power >>= 1;
        }
        return result;
    }

    private long[][] createIdentityMatrix() {
        long[][] matrix = new long[SIZE][SIZE];
        for (int i = 0; i < SIZE; i++) {
            matrix[i][i] = 1;
        }
        return matrix;
    }

    private long[][] multiplyMatrices(long[][] a, long[][] b) {
        long[][] result = new long[SIZE][SIZE];
        for (int i = 0; i < SIZE; i++) {
            for (int k = 0; k < SIZE; k++) {
                if (a[i][k] == 0) continue;
                for (int j = 0; j < SIZE; j++) {
                    result[i][j] = (result[i][j] + a[i][k] * b[k][j]) % MOD;
                }
            }
        }
        return result;
    }

    private long[] multiplyVectorMatrix(long[] vector, long[][] matrix) {
        long[] result = new long[SIZE];
        for (int j = 0; j < SIZE; j++) {
            for (int i = 0; i < SIZE; i++) {
                result[j] = (result[j] + vector[i] * matrix[i][j]) % MOD;
            }
        }
        return result;
    }
}

总结

通过这个问题,我们学习了:

  1. 如何将字符串转换问题建模为矩阵运算

  2. 使用矩阵快速幂高效处理多次转换

  3. 如何处理大数取模问题

这种方法不仅适用于这个问题,还可以推广到其他类似的线性变换问题中。掌握矩阵快速幂技术对解决算法竞赛中的许多问题都大有裨益。


网站公告

今日签到

点亮在社区的每一天
去签到