依旧前缀和,这题本来应该是昨天做的,没做出来,一直拖到今天,看了题解几次三番,总算是自己写了出来,心态炸了。
链接如下 3361. 两个字符串的切换距离。
以下是做这道题的经过
没有尝试用暴力的做法。题目主要围绕两个前缀和数组的构建以及计算时的一些细节。
最开始做的尝试是在环形数组的基础上,讨论s[i]和t[i]的大小,然后减来减去,三元表达式写了很多,密密麻麻的,把我自己也给绕进去了,其实仔细写感觉应该也是能写出来?就是会有点太复杂了,脑袋大。
后来看了灵神的题解,把环形数组的元素重复了一次,相当与把数组复制了一遍,长度扩大一倍,化曲为直了,简化了问题。
前缀和数组nextCnt[53],prevCnt[53],这里假定大家都有前缀和的基础,过多的我不赘述,我也不会讲,可以参照灵神在303这道题的题解来辅助理解303. 区域和检索 - 数组不可变 - 力扣(LeetCode)
按照上面提到的的思路,构建完前缀和数组后来讨论如何计算,主要是根据s[i]和t[i]的大小来判断,我们可以简化一下s[i]和t[i],因为从题目知道s[i]是需要被改变的字符,t[i]是目标字符,s[i].t[i] ∈[a-z]所以我们可以记 int from = s[i]-'a' , to = t[i]-'a';来表示字符的序号,方便表示。
对于s中的每个字符s[i],我们需要比较他通过向前走或者向后走变为t[i]的最小花费,局部最优来达到全局最优,所以需要比较对应的前缀和prev和next。
前缀和数组是左闭右开的,所以next减去只需next[to>from?to:to+26]-next[from],
而对于prev虽然也是左闭右开的,但因为方向的问题prev[to>from?from+27:from]-prev[to+!],为什么+1画个图会更方便理解一下。
总结一下,主要做了两件事,一个是将数组复制一下,避免了环形数组讨论麻烦,另一个是将s[i]和t[i]简化为from和to,便于去判断。
C++代码如下:
class Solution {
public:
long long shiftDistance(string s, string t, vector<int>& nextCost,
vector<int>& previousCost) {
int cNum = 26;
long long nextcnt[53]{}, precnt[53]{};
for (int i = 0; i < 52; i++) {
nextcnt[i + 1] = nextcnt[i] + nextCost[i % 26];
precnt[i + 1] = precnt[i] + previousCost[i % 26];
}
long long ans = 0;
for (int i = 0; i < s.length(); i++) {
int from = s[i] - 'a';
int to = t[i] - 'a';
ans += min((from > to ? nextcnt[to + 26] - nextcnt[from]
: nextcnt[to] - nextcnt[from]),
(from > to ? precnt[from + 1] - precnt[to + 1]
: precnt[from + 27] - precnt[to + 1]));
}
return ans;
}
};