不同字符的最小子序列
难度:
标签:
题目描述
代码结果
运行时间: 24 ms, 内存: 16.0 MB
/**
* 思路:
* 1. 依旧是利用栈来维护字典序最小的子序列。
* 2. 使用Java Stream API处理字符串并操作栈。
*/
import java.util.*;
import java.util.stream.Collectors;
public class SolutionStream {
public String smallestSubsequence(String s) {
int[] lastIndex = new int[26]; // 记录每个字符最后出现的位置
Arrays.stream(s.split(""))
.forEach(c -> lastIndex[c.charAt(0) - 'a'] = s.lastIndexOf(c.charAt(0)));
boolean[] inStack = new boolean[26]; // 记录字符是否在栈中
Deque<Character> stack = new ArrayDeque<>();
s.chars()
.mapToObj(c -> (char) c)
.forEach(c -> {
if (inStack[c - 'a']) return;
while (!stack.isEmpty() && stack.peek() > c && lastIndex[stack.peek() - 'a'] > s.indexOf(c)) {
inStack[stack.pop() - 'a'] = false;
}
stack.push(c);
inStack[c - 'a'] = true;
});
return stack.stream()
.map(String::valueOf)
.collect(Collectors.joining());
}
}
解释
方法:
该解法使用贪心算法和栈来解决问题。首先,通过遍历字符串s建立每个字符的最后出现位置的哈希表,这有助于确定字符是否可以从栈中弹出。算法遍历字符串s中的每个字符,使用栈来构建最终的结果序列。如果当前字符未在结果序列中,且当前字符字典序小于栈顶字符,并且栈顶字符在后面还会出现,那么栈顶字符会被弹出。这保证了序列尽可能地小在字典序中。使用集合seen来跟踪哪些字符已经被加入到栈中,防止重复添加字符。
时间复杂度:
O(n)
空间复杂度:
O(n)
代码细节讲解
🦆
如何确保通过遍历字符串来建立每个字符的最后出现位置的哈希表时,该表确实记录了每个字符的最后位置而不是第一个位置?
▷🦆
在提到栈顶字符在后面还会出现时,是如何通过算法中的数据结构确定这一点的?
▷🦆
为什么在字符比栈顶元素字典序小的情况下就必须弹出栈顶元素?这样的操作是否可能导致最终结果不是最小字典序?
▷🦆
在解法中,seen集合被用于跟踪哪些字符已加入栈中。如果字符已经在seen集合中了,为什么就不需要再次将其添加到栈中?
▷