提示
给你两个下标从 0 开始的字符串 source 和 target ,它们的长度均为 n 并且由 小写英文字母组成。
另给你两个下标从 0 开始的字符串数组 original 和 changed ,以及一个整数数组 cost ,其中 cost[i] 代表将字符串 original[i] 更改为字符串 changed[i] 的成本。
你从字符串 source 开始。在一次操作中,如果 存在 任意 下标 j 满足 cost[j] == z 、original[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 。
注意 ,可能存在下标 i 、j 使得 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;
}
};