有一个 单线程 CPU 正在运行一个含有 n 道函数的程序。每道函数都有一个位于 0 和 n-1 之间的唯一标识符。
函数调用 存储在一个 调用栈 上 :当一个函数调用开始时,它的标识符将会推入栈中。而当一个函数调用结束时,它的标识符将会从栈中弹出。标识符位于栈顶的函数是 当前正在执行的函数 。每当一个函数开始或者结束时,将会记录一条日志,包括函数标识符、是开始还是结束、以及相应的时间戳。
给你一个由日志组成的列表 logs ,其中 logs[i] 表示第 i 条日志消息,该消息是一个按 “{function_id}:{“start” | “end”}:{timestamp}” 进行格式化的字符串。例如,“0:start:3” 意味着标识符为 0 的函数调用在时间戳 3 的 起始开始执行 ;而 “1:\end:2” 意味着标识符为 1 的函数调用在时间戳 2 的 末尾结束执行。注意,函数可以 调用多次,可能存在递归调用 。
函数的 独占时间 定义是在这个函数在程序所有函数调用中执行时间的总和,调用其他函数花费的时间不算该函数的独占时间。例如,如果一个函数被调用两次,一次调用执行 2 单位时间,另一次调用执行 1 单位时间,那么该函数的 独占时间 为 2 + 1 = 3 。
以数组形式返回每个函数的 独占时间 ,其中第 i 个下标对应的值表示标识符 i 的函数的独占时间。
使用栈来模拟执行过程:当一个函数被调用(op = start)时,压入栈内,当函数调用完成(op = end)时,从栈顶弹出(此时栈顶元素必然是该结束函数的入栈记录),使用变量 cur 记录当前时间。
从前往后处理所有的 log[i],根据 log[i]是属于函数调用还是函数结束进行分情况讨论:
class Solution {
public int[] exclusiveTime(int n, List<String> logs) {
int[] ans = new int[n];
Deque<Integer> d = new ArrayDeque<>();
int cur = -1;
for (String log : logs) {
String[] ss = log.split(":");
int idx = Integer.parseInt(ss[0]), ts = Integer.parseInt(ss[2]);
if (ss[1].equals("start")) {
if (!d.isEmpty()) ans[d.peekLast()] += ts - cur;
d.addLast(idx);
cur = ts;
} else {
int func = d.pollLast();
ans[func] += ts - cur + 1;
cur = ts + 1;
}
}
return ans;
}
}
一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响小偷偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组 nums ,请计算 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
动态规划。定义数组dp,dp[i]表示第i-1天可以偷窃到的最高金额,初始化dp[0]=nums[0], dp[1]=max(nums[0], nums[1]),递推公式为dp[n] = max(nums[n]+dp[n-2], dp[n-1])
class Solution {
public int rob(int[] nums) {
if (nums.length == 0) return 0;
if (nums.length == 1) return nums[0];
int[] dp = new int[nums.length];
dp[0] = nums[0];
dp[1] = Math.max(nums[0], nums[1]);
for (int i = 2; i < nums.length; i++) {
dp[i] = Math.max(nums[i]+dp[i-2], dp[i - 1]);
}
return dp[nums.length-1];
}
}
特殊的二进制序列是具有以下两个性质的二进制序列:
给定一个特殊的二进制序列 S,以字符串形式表示。定义一个操作为首先选择 S 的两个连续且非空的特殊的子串,然后将它们交换。(两个子串为连续的当且仅当第一个子串的最后一个字符恰好为第二个子串的第一个字符的前一个字符。)
在任意次数的操作之后,交换后的字符串按照字典序排列的最大的结果是什么?
由于具备的两个性质可得:序列的第一个数一定是1,最后一个数一定是0;任意前缀中 1 的数量一定大于等于 0 的数量。
由此定义每个字符的得分:字符 1 得分为 1 分,字符 0 得分为 -1 分。给定字符串 s 的总得分为 0,且任意前缀串不会出现得分为负数的情况。考虑将 s 进行划分为多个足够小特殊字符串 item(足够小的含义为每个 item 无法再进行划分),每个 item 的总得分为 0。根据 s 定义,必然可恰好划分为多个 item。每次操作可以将相邻特殊字符串进行交换,于是问题转换为将 s 进行重排,求其重排后字典序最大的方案。
class Solution {
public String makeLargestSpecial(String s) {
if (s.length() == 0) return "";
List<String> list = new ArrayList<>();
int count = 0, last = 0;
for (int i = 0, cur=0; i < s.length(); i++, cur++) {
if (s.charAt(i) == '1') count++;
else count--;
if (count == 0){
String string = "1" + makeLargestSpecial(s.substring(last+1, cur))+"0";
list.add(string);
last = cur+1;
}
}
list.sort(Comparator.reverseOrder());
StringBuffer sb = new StringBuffer();
for (String s1 : list) {
sb.append(s1);
}
return sb.toString();
}
}
给定两个字符串 s 和 t ,编写一个函数来判断它们是不是一组变位词(字母异位词)。
注意:若 s 和 t 中每个字符出现的次数都相同且字符顺序不完全相同,则称 s 和 t 互为变位词(字母异位词)。
首先要判断s != t,然后统计各自字符串中字母出现的次数,判断是否相等。
public boolean isAnagram(String s, String t) {
if (s.length() != t.length()) return false;
if (s.equals(t)) return false;
int[] a = new int[26];
int[] b = new int[26];
for (int i = 0; i < s.length(); i++) {
a[s.charAt(i) - 'a']++;
b[t.charAt(i) - 'a']++;
}
return Arrays.equals(a, b);
}
给定一个字符串数组 strs ,将 变位词 组合在一起。 可以按任意顺序返回结果列表。
注意:若两个字符串中每个字符出现的次数都相同,则称它们互为变位词。
首先定义一个map用来存储字母组合以及其该字母组合的字符串。遍历strs,每得到一个str首先对其排序得到对应的key,然后把(key,str)存入map。最后返回map的每个value。
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
if (strs.length == 0) return null;
HashMap<String, List<String>> map = new HashMap<>();
for (String str : strs){
char[] chars = str.toCharArray();
Arrays.sort(chars);
String key = Arrays.toString(chars);
List<String> value = map.getOrDefault(key, new ArrayList<>());
value.add(str);
map.put(key, value);
}
return new ArrayList<List<String>>(map.values());
}
}