力扣每日一题

2977. 转换字符串的最小成本 II

提示

给你两个下标从 0 开始的字符串 sourcetarget ,它们的长度均为 n 并且由 小写英文字母组成。

另给你两个下标从 0 开始的字符串数组 originalchanged ,以及一个整数数组 cost ,其中 cost[i] 代表将字符串 original[i] 更改为字符串 changed[i] 的成本。

你从字符串 source 开始。在一次操作中,如果 存在 任意 下标 j 满足 cost[j] == zoriginal[j] == x 以及 changed[j] == y ,你就可以选择字符串中的 子串 x 并以 z 的成本将其更改为 y 。 你可以执行 任意数量 的操作,但是任两次操作必须满足以下两个 条件 之一

  • 在两次操作中选择的子串分别是 source[a..b]source[c..d] ,满足 b < c d < a 。换句话说,两次操作中选择的下标不相交
  • 在两次操作中选择的子串分别是 source[a..b]source[c..d] ,满足 a == c b == d 。换句话说,两次操作中选择的下标相同

返回将字符串 source 转换为字符串 target 所需的最小 成本。如果不可能完成转换,则返回 -1

注意 ,可能存在下标 ij 使得 original[j] == original[i]changed[j] == changed[i]

思路:

和转化字符串的最小成本I很相似,只是可以转化的对象从一个个单一字符变成了字符串,且不允许存在两次转化中存在重复位置(或者均是对同段字符串进行转换)。

这里面,需要额外考虑的其实只有"在两次操作中选择的子串分别是 source[a..b]source[c..d] ,满足 b < c d < a 。换句话说,两次操作中选择的下标不相交。"这个条件,因为别的条件下,和转化字符串的最小成本I做法是完全一致的,只是dis中存放下标需要对应相应的字符串而已。

那么,首先解决如何让字符串对应整数类型的下标,这里采用的是Tire树,即利用从树的根节点出发,根据不同的字母进入不同的子树,根节点中存放对应字符串的 整数类型。

其次,要解决如何满足"在两次操作中选择的子串分别是 source[a..b]source[c..d] ,满足 b < c d < a 。换句话说,两次操作中选择的下标不相交。 "这个条件,这里采用的方式是利用递推的方式,设置f[i]表示从下标i开始进行变换,一直到结尾的最小代价,通过严格设置条件从i开始变化,这样当从i到j下标的字符串被改变,花费是cost,其取值应该是f[i]=min(f[i],f[j+1]+cost)。这样就能确保下一次变换是从j+1位置开始的。此外,为了简化计算,这里规定从自己到自己也可以是一种变换,这样就可以有当前下标i对应resource[i]和target[i]一致时,f[i]=f[i+1]。

此外,还需要注意的是,由于

  • 1 <= cost.length == original.length == changed.length <= 100,且
  • 1 <= cost[i] <= 10^6

因此,极限情况下,可能存在转换的最终代价变成10^8,因此对于f的设置和最后return那里的判断,采用和LONG_LONG_MAX /2进行比较。

下面是具体公式:

复制代码
class Solution {
public:

    struct Node{
        Node*son[26]{};
        int sid=-1;
    };
    long long minimumCost(string source, string target, vector<string>& original, vector<string>& changed, vector<int>& cost) {
        Node*root=new Node();
        int sid=0;
        auto put=[&](string&s)->int{
            Node *o=root;
            for(auto&e:s)
            {
                int i=e-'a';
                if(o->son[i]==nullptr)
                o->son[i]=new Node();
                o=o->son[i];
            }
            if(o->sid<0)
            o->sid=sid++;
            return o->sid;
        };

        int m=cost.size();
        vector dis(m * 2, vector<int>(m * 2, INT_MAX / 2));
        for(int i=0;i<m*2;i++)
        dis[i][i]=0;

        for(int i=0;i<m;i++)
        {
            int x=put(original[i]);
            int y=put(changed[i]);
            dis[x][y]=min(dis[x][y],cost[i]);
        }

        for(int k=0;k<sid;k++)
        for(int i=0;i<sid;i++)
        for(int j=0;j<sid;j++)
        {
            if(dis[i][k]==INT_MAX/2||dis[k][j]==INT_MAX/2)
              continue;
            dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
        }

        int n=source.size();
        vector<long long>f(n+1);
        for(int i=n-1;i>=0;i--)
        {
            f[i]=(source[i]==target[i]?f[i+1]:LONG_LONG_MAX / 2);
            Node*p=root;
            Node*q=root;

            for(int j=i;j<n;j++)
            {
                p=p->son[source[j]-'a'];
                q=q->son[target[j]-'a'];
                if(p==nullptr||q==nullptr)
                break;

                if(p->sid<0||q->sid<0) //表示有结尾
                continue;

                int d=dis[p->sid][q->sid];
                if(d<INT_MAX/2)
                f[i]=min(f[i], dis[p->sid][q->sid] + f[j + 1]);
            }    
            
        }
       
        return f[0]<LONG_LONG_MAX / 2?f[0]:-1;
    }
};
相关推荐
圣保罗的大教堂7 分钟前
leetcode 1855. 下标对中的最大距离 中等
leetcode
智者知已应修善业1 小时前
【51单片机按键调节占空比3位数码管显示】2023-8-24
c++·经验分享·笔记·算法·51单片机
JasmineX-12 小时前
数据结构(笔记)——双向链表
c语言·数据结构·笔记·链表
.5482 小时前
## Sorting(排序算法)
python·算法·排序算法
wuweijianlove2 小时前
算法的平均复杂度建模与性能回归分析的技术7
算法·数据挖掘·回归
子琦啊2 小时前
【算法复习】字符串 | 两个底层直觉,吃透高频题
linux·运维·算法
code_pgf4 小时前
Octo 算法详解-开源通用机器人策略模型技术报告
算法·机器人·开源
嘻嘻哈哈樱桃4 小时前
牛客经典101题题解集--动态规划
java·数据结构·python·算法·职场和发展·动态规划
脱氧核糖核酸__4 小时前
LeetCode热题100——234.回文链表(两种解法)
c++·算法·leetcode·链表
IronMurphy4 小时前
【算法四十二】118. 杨辉三角 198. 打家劫舍
算法