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

引言

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

问题描述

给定:

  • 一个由小写字母组成的字符串 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. 如何处理大数取模问题

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/80951.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue2 elementUI 二次封装命令式表单弹框组件

需求&#xff1a;封装一个表单弹框组件&#xff0c;弹框和表单是两个组件&#xff0c;表单组件以插槽的形式动态传入弹框组件中。 外部组件使用的方式如下&#xff1a; 直接上代码&#xff1a; MyDialog.vue 弹框组件 <template><el-dialog:titletitle:visible.syn…

React Hooks:从“这什么鬼“到“真香“的奇幻之旅

写在前面:一个让React老手都拍案叫绝的魔法 “等等,函数组件怎么能有状态?!” —— 这是2018年我第一次听说React Hooks时的反应。当时我正在用class组件写一个复杂的表单,生命周期方法乱得像一碗意大利面。直到我看到了这段代码: function Counter() {const [count, s…

论文阅读笔记——双流网络

双流网络论文 视频相比图像包含更多信息&#xff1a;运动信息、时序信息、背景信息等等。 原先处理视频的方法&#xff1a; CNN LSTM&#xff1a;CNN 抽取关键特征&#xff0c;LSTM 做时序逻辑&#xff1b;抽取视频中关键 K 帧输入 CNN 得到图片特征&#xff0c;再输入 LSTM&…

SpringBoot Vue MySQL酒店民宿预订系统源码(支付宝沙箱支付)+代码讲解视频

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

右值引用的学习

传统的C语法中就有引用的语法&#xff0c;而C11中新增了的右值引用语法特性&#xff0c;所以从现在开始我们之前学习的引用就叫做左值引用。无论左值引用还是右值引用&#xff0c;都是给对象取别名。 左值引用和右值引用 在讲之前&#xff0c;我们先来看一下什么是左值和右值…

PHP黑白胶卷底片图转彩图功能 V2025.05.15

关于底片转彩图 传统照片底片是摄影过程中生成的反色图像&#xff0c;为了欣赏照片&#xff0c;需要通过冲印过程将底片转化为正像。而随着数字技术的发展&#xff0c;我们现在可以使用数字工具不仅将底片转为正像&#xff0c;还可以添加色彩&#xff0c;重现照片原本的色彩效…

【Three.js基础学习】36.particles-morphing-shader

前言 通过着色器如何实现粒子之间动态切换 一、代码 script.js import * as THREE from three import { OrbitControls } from three/addons/controls/OrbitControls.js import { GLTFLoader } from three/addons/loaders/GLTFLoader.js import { DRACOLoader } from three/a…

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】附录-D. 扩展插件列表(PostGIS/PostgREST等)

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 附录D. PostgreSQL扩展插件速查表一、插件分类速查表二、核心插件详解三、安装与配置指南四、应用场景模板五、版本兼容性说明六、维护与优化建议七、官方资源与工具八、附录…

【Linux】冯诺依曼体系结构和操作系统的理解

目录 冯诺依曼体系结构一个例子来深入理解 初识操作系统操作系统的作用设计操作系统的目的操作系统之上和之下分别有啥 管理的精髓&#xff0c;先描述&#xff0c;再组织 冯诺依曼体系结构 我们知道&#xff0c;计算机这个东西发明出来就是帮助人们快速解决问题的。那如果我们想…

kotlin @JvmStatic注解的作用和使用场景

1. JvmStatic 的作用 JvmStatic 是 Kotlin 提供的一个注解&#xff0c;用于在 JVM 上将伴生对象&#xff08;companion object&#xff09;中的方法或属性暴露为 Java 静态方法或字段。 作用对象&#xff1a;只能用在 companion object 中的函数或属性。效果&#xff1a; 在 …

Redis实现-优惠卷秒杀(基础版本)

(一)全局唯一ID 一、全局ID生成器 可以看到在优惠卷订单表中的主键id并没有设置Auto increment自增长 假如未来订单量达到数亿单&#xff0c;单表无法保存如此多数据&#xff0c;就需要对其进行分表存储(分布式)。假如每张表都采用自增长&#xff0c;各自从1开始自增&#xf…

c++STL——哈希表封装:实现高效unordered_map与unordered_set

文章目录 用哈希表封装unordered_map和unordered_set改进底层框架迭代器实现实现思路迭代器框架迭代器重载operator哈希表中获取迭代器位置 哈希表的默认成员函数修改后的哈希表的代码封装至上层容器 用哈希表封装unordered_map和unordered_set 在前面我们已经学过如何实现哈希…

虹科应用 | 探索PCAN卡与医疗机器人的革命性结合

随着医疗技术的不断进步&#xff0c;医疗机器人在提高手术精度、减少感染风险以及提升患者护理质量方面发挥着越来越重要的作用。医疗机器人的精确操作依赖于稳定且高效的数据通信系统&#xff0c;虹科提供的PCAN四通道mini PCIe转CAN FD卡&#xff0c;正是为了满足这一需求而设…

Yolov8的详解与实战-深度学习目标检测

Yolov8的详解与实战- 文章目录 摘要 模型详解 C2F模块 Loss head部分 模型实战 训练COCO数据集 下载数据集 COCO转yolo格式数据集&#xff08;适用V4&#xff0c;V5&#xff0c;V6&#xff0c;V7&#xff0c;V8&#xff09; 配置yolov8环境 训练 测试 训练自定义数据集 Labelme…

scons user 3.1.2

前言 感谢您抽出时间阅读有关 SCons 的内容。SCons 是一款下一代软件构建工具&#xff0c;或者称为 make 工具&#xff0c;即一种用于构建软件&#xff08;或其他文件&#xff09;并在底层输入文件发生更改时使已构建的软件保持最新状态的软件实用程序。 SCons 最显著的特点是…

Java的多线程笔记

创建一个线程的方法有多种&#xff0c;比如可以继承Thread类或者实现Runnable接口&#xff0c;结论是实现Runnable接口比前者更加优越。 二者代码对比 Java 不支持多继承&#xff0c;如果你继承了 Thread 类&#xff0c;就不能再继承其他类&#xff0c;实现 Runnable 接口后&am…

PDF Base64格式字符串转换为PDF文件临时文件

需求描述&#xff1a; 在对接电子病历系统与河北CA&#xff0c;进行免密文件签章的时候&#xff0c;两者系统入参不同&#xff0c;前者是pdf文件&#xff0c;base64格式&#xff1b;后者要求File类型的PDF文件。 在业务中间层开发时&#xff0c;则需要接收EMR侧提供的base64格式…

代码随想录训练营第二十三天| 572.另一颗树的子树 104.二叉树的最大深度 559.N叉树的最大深度 111.二叉树的最小深度

572.另一颗树的子树&#xff1a; 状态&#xff1a;已做出 思路&#xff1a; 这道题目当时第一时间不是想到利用100.相同的树思路来解决&#xff0c;而是先想到了使用kmp&#xff0c;不过这个题目官方题解确实是有kmp解法的&#xff0c;我使用的暴力解法&#xff0c;kmp的大致思…

【RabbitMq C++】消息队列组件

RabbitMq 消息队列组件 1. RabbitMq介绍2. 安装RabbitMQ3. 安装 RabbitMQ 的 C客户端库4. AMQP-CPP 库的简单使用4.1 使用4.1.1 TCP 模式4.1.2 扩展模式 4.2 常用类与接口介绍4.2.1 Channel4.3.2 ev 5. RabbitMQ样例编写5.1 发布消息5.2 订阅消息 1. RabbitMq介绍 RabbitMq - …

鸿蒙NEXT开发动画案例8

1.创建空白项目 2.Page文件夹下面新建Spin.ets文件&#xff0c;代码如下&#xff1a; /*** SpinKit动画组件 (重构版)* author: CSDN-鸿蒙布道师* since: 2025/05/14*/interface AnimationGroup {indexes: number[];delay: number; }ComponentV2 export struct SpinEight {Re…