力扣每日一题

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;
    }
};
相关推荐
只是懒得想了5 小时前
C++实现密码破解工具:从MD5暴力破解到现代哈希安全实践
c++·算法·安全·哈希算法
码农水水5 小时前
得物Java面试被问:消息队列的死信队列和重试机制
java·开发语言·jvm·数据结构·机器学习·面试·职场和发展
m0_736919105 小时前
模板编译期图算法
开发语言·c++·算法
dyyx1115 小时前
基于C++的操作系统开发
开发语言·c++·算法
m0_736919105 小时前
C++安全编程指南
开发语言·c++·算法
蜡笔小马5 小时前
11.空间索引的艺术:Boost.Geometry R树实战解析
算法·r-tree
-Try hard-5 小时前
数据结构:链表常见的操作方法!!
数据结构·算法·链表·vim
2301_790300965 小时前
C++符号混淆技术
开发语言·c++·算法
我是咸鱼不闲呀5 小时前
力扣Hot100系列16(Java)——[堆]总结()
java·算法·leetcode
wengqidaifeng6 小时前
数据结构---顺序表的奥秘(下)
c语言·数据结构·数据库