引言
在編程競賽和算法問題中,我們經常會遇到需要對字符串進行多次轉換的問題。本文將介紹一個有趣的問題:給定一個字符串和轉換規則,計算經過多次轉換后字符串的長度。由于直接模擬會導致性能問題,我們將使用矩陣快速冪來高效解決這個問題。
問題描述
給定:
-
一個由小寫字母組成的字符串?
s
-
一個整數?
t
?表示轉換次數 -
一個長度為26的數組?
nums
,其中?nums[i]
?表示字母?'a'+i
?的轉換規則
轉換規則:
-
將?
s[i]
?替換為字母表中后續的?nums[s[i]-'a']
?個連續字符 -
如果超過?
'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),這種方法會非常低效甚至不可行,因為字符串長度會呈指數級增長。
矩陣快速冪解法
思路概述
-
統計字符頻率:記錄初始字符串中每個字符的出現次數
-
構建轉換矩陣:表示每個字符如何轉換為其他字符
-
矩陣快速冪:高效計算轉換矩陣的?
t
?次冪 -
計算最終長度:將初始頻率向量與轉換矩陣相乘,求和得到最終長度
詳細步驟
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;} }
總結
通過這個問題,我們學習了:
-
如何將字符串轉換問題建模為矩陣運算
-
使用矩陣快速冪高效處理多次轉換
-
如何處理大數取模問題
這種方法不僅適用于這個問題,還可以推廣到其他類似的線性變換問題中。掌握矩陣快速冪技術對解決算法競賽中的許多問題都大有裨益。