1208.尽可能使字符串相等

85 阅读1分钟

题目:
给你两个长度相同的字符串,s 和 t

将 s 中的第 i 个字符变到 t 中的第 i 个字符需要 |s[i] - t[i]| 的开销(开销可能为 0),也就是两个字符的 ASCII 码值的差的绝对值。

用于变更字符串的最大预算是 maxCost。在转化字符串时,总开销应当小于等于该预算,这也意味着字符串的转化可能是不完全的。

如果你可以将 s 的子字符串转化为它在 t 中对应的子字符串,则返回可以转化的最大长度。

如果 s 中没有子字符串可以转化成 t 中对应的子字符串,则返回 0

算法:

方法一:双指针
双指针left,right,保证[left,right]之间的字符的剩余maxCost>=0得到有效的字符,计算字符长度

func equalSubstring(s string, t string, maxCost int) int {
	ans := 0

	for left, right := 0, 0 ; right < len(s) && right < len(t); right ++ {
		maxCost = maxCost - abs(int(s[right]) - int(t[right]))
		// fmt.Println(maxCost, s[right],t[right], int(s[right] - t[right]))
		for maxCost < 0 {
			maxCost = maxCost + abs(int(s[left]) - int(t[left]))
			left ++
		}
		// maxCost >= 0时,计算长度
		if right - left + 1 > ans {
			ans = right - left + 1
		}
	}
	return ans
}

func abs(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

方法二:二分查找
计算模型:我们只需要找出成本不超过 maxCost 的最大长度区间,为什么想到用二分呢,因为假设答案字符串的长度为ans,则长度小于等于ans也满足题意,而长度大于ans的字符串长度均不满足,存在二段性,就可以猜猜猜,于是可以使用二分。至于这个ans长度的起始位置,其实我并不关心。
构造cost的前缀和sum是为了在O(1)时间求出cost

func equalSubstring(s string, t string, maxCost int) int {
	n := len(s)
	sum := make([]int, n + 1) 
	for i := range s {
		sum[i + 1] = sum[i] + abs(int(s[i]) - int(t[i]))
	}
	left, right := 1, n
	// left right相等时意味着一个有效的结果,有效结果的有边界
	// 再比这个边界大一点,就超过maxCost了
	for left < right {
		// 其实mid没必要完全1/2,只是1/3,1/4效率不够高,mid左右偏移一没有影响 
		// 设置mid := (left + right + 1) >> 1,是因为(left + right) >> 1是向0(左取整)
		// 为了mid=(left + right) >> 1时left = mid防止陷入死循环,小技巧get
		mid := (left + right + 1) >> 1
		// fmt.Println(left, mid, right)
		// 猜有效的子字符串长度为mid,cost不超过maxCost是否有解
		if check(sum, mid, maxCost) {
			left = mid
		} else {
			right = mid - 1
		}
	}
	// 为什么返回left呢?因为left是mid赋值的,而mid是有效长度
        // 你也可以用另一个变量存储mid
	if check(sum, left, maxCost) {
		return left
	}
	return 0
}

// sum[mid,n]是否存在sum[i] - sum[mid] <= max
func check(sum []int, mid, max int) bool {
	for i := mid; i < len(sum); i ++ {
		// 修改nums[i-mid,i]的总cost是否小于maxCost 
		if sum[i] - sum[i - mid] <= max {
			return true
		}
	}
	return false
}

func abs(a int) int {
	if a < 0 {
		return -1 * a
	}
	return a
}