日常: 算法、数据结构、分布式系统、日常思考感悟等

[LeetCode] Remove All Adjacent Duplicates in String II

2021.04.22

问题描述

You are given a string s and an integer k, a k duplicate removal consists of choosing k adjacent and equal letters from s and removing them, causing the left and the right side of the deleted substring to concatenate together.

We repeatedly make k duplicate removals on s until we no longer can.

Return the final string after all such duplicate removals have been made. It is guaranteed that the answer is unique.

Example 1:

Input: s = “abcd”, k = 2

Output: “abcd”

Explanation: There’s nothing to delete.

Example 2:

Input: s = “deeedbbcccbdaa”, k = 3

Output: “aa”

Explanation:

First delete “eee” and “ccc”, get “ddbbbdaa” Then delete “bbb”, get “dddaa” Finally delete “ddd”, get “aa”

Example 3:

Input: s = “pbbcggttciiippooaais”, k = 2

Output: “ps”

Constraints:

  • 1 <= s.length <= 10^5
  • 2 <= k <= 10^4
  • s only contains lower case English letters.

问题分析

这个问题容易让人联想到俄罗斯方块,拼好一层消掉;类似的,这里我们采用stack来解决这个问题

相比较于普通的stack,这次我们需要对元素类型做一下自定义:

type item struct {
    val              byte // 字符
    consecutiveCount int  // 该字符连续出现的次数
}

每次往stack里Push时,进行如下判断:

  1. 如果当前栈顶元素和待push的元素相同,则不必push,只是连续出现次数+1;
  2. 如果不同,直接push; 同时连续出现次数初始化为1
  3. 判断栈顶字符的连续出现次数是否等于指定的k,如果等于,pop即可

全部字符处理完后,stack中剩下的即为所求。全部pop出来,逆序输出即可。

示例代码–stack版

// stack
func removeDuplicates(s string, k int) string {
	stack := stack{}
	for i := 0; i < len(s); i++ {
		stack.Push(s[i], k)
	}
	ans := []byte{}
	for {
		item := stack.Pop()
		if item == nil {
			break
		}
		ans = append([]byte{item.val}, ans...)
	}
	return string(ans)
}

type stack struct {
	list []*item
}

type item struct {
	val              byte
	consecutiveCount int
}

func (s *stack) Pop() *item {
	if len(s.list) == 0 {
		return nil
	}
	ans := s.list[len(s.list)-1]
	s.list = s.list[:len(s.list)-1]
	return ans
}

func (s *stack) Push(v byte, k int) {
	item := item{
		val:              v,
		consecutiveCount: 1,
	}
	if len(s.list) > 0 {
		last := s.list[len(s.list)-1]
		if last.val == v {
			// pop
			if last.consecutiveCount == k-1 {
				s.list = s.list[:len(s.list)-k+1]
				return
			}
			item.consecutiveCount = last.consecutiveCount + 1
		}
	}
	s.list = append(s.list, &item)
}

示例代码–2 pointers版

stack版的解法提交AC后,在评论区发现还有另外一种解法,利用字符数组+双指针去模拟刚才stack的行为,具体逻辑如下:

  1. 初始化2个指针i,j; i表示栈顶; j表示当前遍历到的字符
  2. 定义一个计数map,记为m
  3. 首先,rs[i] = rs[j]; 表示将当前遍历的字符压入栈
  4. 如果 rs[i-1]与rs[j]相同,此时 m[i]计数在m[i-1]基础上+1; 否则初始化为1即可
  5. 如果 m[i] == k,说明栈顶k个元素相同,此时直接将i回退k个位置

以上步骤执行完,返回字符数组的子数组 rs[0:i+1], 转为字符串即为所求

// 2 pointers
func removeDuplicatesV2(s string, k int) string {
	rs := []rune(s)
	i, lens := -1, len(s)
	m := make(map[int]int)
	for j := 0; j < lens; j++ {
		i++
		rs[i] = rs[j]
		if i > 0 && rs[i-1] == rs[j] {
			m[i] = m[i-1] + 1
		} else {
			m[i] = 1
		}
		if m[i] == k {
			i -= k
		}
	}

	return string(rs[0 : i+1])
}


原题链接

Remove All Adjacent Duplicates in String II

发表评论