字符串转换后的长度

简介: 【10月更文挑战第10天】字符串转换后的长度I,字符串转换后的长度II

3335. 字符串转换后的长度 I

这个难度直接可以用dp去解决我们看

a->b

b->c

………

z->ab

那么我们用代码d[i]表示i这个字符的个数,那么从上就得出

dp[i]['a']=dp[i-1]['z'];

dp[i]['b']=dp[i-1]['a']+dp[i]['z'];

dp[i]['c']=dp[i-1]['b'];

dp[i]['z']=dp[i-1]['y];

可以看出从c-z我们都可以直接从前一个继承过来,ab我们单独去操作即可

所以我们遍历每一步t然后对于每一步将字符串里的a-z操作就行,我们只需要知道数量,并不需要知道具体位置。

所以时间复杂度就是O(26*t)

class Solution {
public:
    int lengthAfterTransformations(string s, int t) {
        //用dp[i][j]表示操作i次后,字符j在字符串中出现的次数,
        /**
        dp[i]['a']=dp[i-1]['z'];
        dp[i]['b']=dp[i-1]['a']+dp[i]['z'];
        dp[i]['c']=dp[i-1]['b'];
        dp[i]['z']=dp[i-1]['y];
        */
        const int MOD = 1e9+7;
        vector<int> a(26,0);
        for(auto it:s)a[it-'a']++;//记录每个字符出现的次数
        for(int i=1;i<=t;i++){
            vector<int> tmp(26,0);
            for(int j=0;j<25;j++) tmp[j+1]=a[j];//继承过来b-z
            tmp[0]=a[25];//a从z继承过来
            tmp[1]=(tmp[1]+a[25])%MOD;//除了从a继承过来的还有z继承过来的
            a=tmp;
        }
        long long ans=0;
        for(int i=0;i<26;i++) ans=(ans+a[i])%MOD;
        return ans;
    }
};

难度升级:矩阵快速幂优化状压DP

分析:

首先知道初级难度的用dp去维护,我们这里也是先去找子问题,看看能不能拆解出子问题:

子问题:

假设a经过过一次,之后变成bc。问题就变成了计算b替换(t-1)次的长度,加上,c替换(t-1)次的长度。

状态定义和状态转移:

那么我们还用dp[i][j]表示字母j替换i次的长度

上面说的例子:dp[t][0]=dp[i-1][1]+dp[i-1][2]

那么我们做推广一下:

c=nums[i]
$$ dp[i][j]=\sum^{j+c}_{k=j+1}dp[i-1][k\%26] $$
k%26是为了z->a这个阶段

初始状态:dp[0][j]=1

最终结果$$\sum_{j=0}^{25}dp[t][j]\times a[j]$$其中a[j]s中字母j的出现次数

这个复杂度就是解决第一题的复杂度O(26*t)

矩阵快速幂优化

如果nums=[1,1,1,1,1,1,1,1,……,1,2]就是第一题的要求,a-y都只产生一个,z产生两个。
$$ dp[i][0]=dp[i-1][1] $$

$$ dp[i][1]=dp[i-1][2] $$

$$ dp[i][25]=dp[i-1][0]+dp[i-1][1] $$

用矩阵乘法表示:
$$ \left[\begin{array}{c} dp[i][0] \\ dp[i][1] \\ dp[i][2] \\ \vdots \\ dp[i][23] \\ dp[i][24] \\ dp[i][25] \end{array}\right]=\left[\begin{array}{ccccccc} 0 & 1 & 0 & 0 & \cdots & 0 & 0 \\ 0 & 0 & 1 & 0 & \cdots & 0 & 0 \\ 0 & 0 & 0 & 1 & \cdots & 0 & 0 \\ \vdots & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots \\ 0 & 0 & 0 & 0 & \cdots & 1 & 0 \\ 0 & 0 & 0 & 0 & \cdots & 0 & 1 \\ 1 & 1 & 0 & 0 & \cdots & 0 & 0 \end{array}\right]\left[\begin{array}{c} dp[i-1][0] \\ dp[i-1][1] \\ dp[i-1][2] \\ \vdots \\ dp[i-1][23] \\ dp[i-1][24] \\ dp[i-1][25] \end{array}\right] $$
把上式中的三个矩阵记作A[i],M,A[i-1]
$$ A[i]=M\times A[i-1] $$
那么就有
$$ \begin{align} A[t] &= M \times A[i-1] \\ &= M \times M \times A[i-2] \\ & ...\\ &=M^t\times A[0] \end{align} $$
其中$M^t$ 可以用矩阵快速幂计算。$A[0]$是长为26的列向量,值全为1,对应着dp[0][j]=1

class Solution {
public:
    static const int MOD=1e9+7;
    vector<vector<int>> mul(vector<vector<int>> a,vector<vector<int>> b){
        vector<vector<int>> c(26,vector<int>(26,0));
        for(int i=0;i<26;i++){
            for(int j=0;j<26;j++){
                for(int k=0;k<26;k++){
                    c[i][j]=(c[i][j] + (long long) a[i][k]*b[k][j]%MOD)%MOD;
                }
            }
        }
        return c;
    }
    vector<vector<int>> pow(vector<vector<int>> a,int n){
        vector<vector<int>> ans(26,vector<int>(26,0));
        for(int i=0;i<26;i++){
            ans[i][i]=1;//初始为单位矩阵
        }
        while(n){
            if(n&1) ans=mul(ans,a);//矩阵相乘
            a=mul(a,a);
            n>>=1;
        }
        return ans;
    }
    int lengthAfterTransformations(string s, int t, vector<int>& nums) {
        vector<vector<int>> m(26,vector<int>(26,0));//M矩阵
        for(int i=0;i<26;i++){
            for(int j=i+1;j<=i+nums[i];j++){//这些地方是i能产生的,也就对应着M矩阵ij处为1
                m[i][j%26]=1;
            }            
        }
        m=pow(m,t);//矩阵快速幂

        vector<int> ans(26,0);//记录s字符串中有        
        for(auto it:s){
            ans[it-'a']++;
        }
        long long res=0;
        for(int i=0;i<26;i++){
            long long tmp=0;
            for(int j=0;j<26;j++){
                tmp=(tmp+m[i][j])%MOD;
            }
            res=(res+tmp*ans[i]%MOD)%MOD;
        }
        return res;

    }
};
目录
相关文章
|
11月前
|
JSON 开发工具 git
精通 Prettier:进阶配置与最佳实践
【10月更文挑战第18天】Prettier 是一款流行的代码格式化工具,它能够帮助开发者保持代码风格的一致性,减少因代码风格争议而产生的争论。本文将深入探讨如何根据项目需求进行详细的配置选项调整,并分享一些使用 Prettier 的最佳实践,包括如何通过 Git 钩子自动化代码格式化过程以及如何解决常见的配置冲突问题。
566 5
|
存储 并行计算 算法
Dask 在科学计算中的角色:加速科研数据分析
【8月更文第29天】在科学研究中,处理和分析大规模数据集的能力对于取得突破性成果至关重要。Dask 是一个灵活的并行计算库,能够与 Python 的科学计算生态系统无缝集成,为科研人员提供了高效处理大规模数据集的手段。本文将介绍如何使用 Dask 加速科研数据分析,并通过具体的代码示例展示其在实际场景中的应用。
380 0
|
11月前
|
前端开发 安全 Java
【Spring】Spring Boot项目创建和目录介绍
【Spring】Spring Boot项目创建和目录介绍
276 2
|
11月前
|
人工智能 Rust Java
10月更文挑战赛火热启动,坚持热爱坚持创作!
开发者社区10月更文挑战,寻找热爱技术内容创作的你,欢迎来创作!
2557 44
|
10月前
|
运维
【10月更文挑战赛】获奖名单出炉,快来看看谁是十月创作明星!
【10月更文挑战赛】获奖名单出炉,快来看看谁是十月创作明星!
309 9
|
监控 网络协议 Linux
Linux利用nc命令脚本批量检测服务器指定端口是否开放
nc命令脚本批量检测服务器指定端口是否开放
1389 0
Linux利用nc命令脚本批量检测服务器指定端口是否开放
|
10月前
|
存储 Java 调度
Java 中的优先队列 PriorityQueue 详解
【10月更文挑战第22天】优先队列 PriorityQueue 是一种非常实用的数据结构,在许多场景中都能发挥重要作用。通过深入了解其特点和用法,我们可以更好地利用它来解决实际问题。
450 13
|
10月前
|
存储 C语言
【数据结构】顺序表(c语言实现)(附源码)
本文介绍了线性表和顺序表的基本概念及其实现。线性表是一种有限序列,常见的线性表有顺序表、链表、栈、队列等。顺序表是一种基于连续内存地址存储数据的数据结构,其底层逻辑是数组。文章详细讲解了静态顺序表和动态顺序表的区别,并重点介绍了动态顺序表的实现,包括初始化、销毁、打印、增删查改等操作。最后,文章总结了顺序表的时间复杂度和局限性,并预告了后续关于链表的内容。
252 3
|
10月前
|
NoSQL Java 中间件
springboot整合常用中间件框架案例
该项目是Spring Boot集成整合案例,涵盖多种中间件的使用示例,每个案例项目使用最小依赖,便于直接应用到自己的项目中。包括MyBatis、Redis、MongoDB、MQ、ES等的整合示例。
337 1
|
10月前
|
弹性计算 应用服务中间件 定位技术
基于地理位置的访问策略的GA加速最佳实践
全球加速GA是阿里云提供的全球网络加速服务,支持基于地理位置的访问策略。本文介绍如何通过多组GA实例组合,实现一个域名在全球多个区域的服务同步加速。具体步骤包括创建ECS实例、部署Nginx服务器、配置GA及全局流量管理器等。
425 5