句子仅由小写字母(‘a’ 到 ‘z’)、数字(‘0’ 到 ‘9’)、连字符(’-’)、标点符号(’!’、’.’ 和 ‘,’)以及空格(’ ')组成。每个句子可以根据空格分解成 一个或者多个 token ,这些 token 之间由一个或者多个空格 ’ ’ 分隔。
如果一个 token 同时满足下述条件,则认为这个 token 是一个有效单词:
给你一个字符串 sentence ,请你找出并返回 sentence 中 有效单词的数目 。
示例:
输入:sentence = "he bought 2 pencils, 3 erasers, and 1 pencil-sharpener."
输出:6
解释:句子中的有效单词是"he"、"bought"、"pencils,"、"erasers,"、
"and"、"pencil-sharpener."
解题思路:根据条件进行单词识别即可。统计单词数量
public int countValidWords(String sentence) {
// 将单词按空格分开
String[] tokens = sentence.split(" ");
int ans = 0;
// f为连接符 ‘-’的标记
boolean f;
// 循环将每个字符串进行单词识别
for(String token: tokens){
char[] chars = token.toCharArray();
f = false;
// 循环识别每个字符
for(int i = 0;i < chars.length;i++){
// 出现数字直接break
if(chars[i] >= '0' && chars[i] <= '9'){
break;
}else if(chars[i] == '-'){
// 出现两个连接符break
if(f == true){
break;
}else{
// 连接符 两边不为字母,break
if(i-1 >= 0 && ((i+1 < chars.length) &&
(chars[i+1]>='a' && chars[i+1]<='z'))){
// f记录出现连接符
f = true;
}else{
break;
}
}
}else if((chars[i] == '!' || chars[i] == '.' || chars[i] == ',')&&
i != chars.length - 1){
// 出现符号,不为最后一个字符,break
break;
}
// 到了最后一个字符,该字符串为合格的单词
if(i == chars.length - 1){
ans++;
}
}
}
return ans;
}
你正在参加一个多角色游戏,每个角色都有两个主要属性:攻击 和 防御 。给你一个二维整数数组 properties ,其中 properties[i] = [attacki, defensei] 表示游戏中第 i 个角色的属性。
如果存在一个其他角色的攻击和防御等级都严格高于该角色的攻击和防御等级,则认为该角色为弱角色 。更正式地,如果认为角色 i 弱于 存在的另一个角色 j ,那么 attackj > attacki 且 defensej > defensei 。
返回弱角色的数量。
方法一:按照某一个属性排序,使用单调栈进行统计。
public int numberOfWeakCharacters(int[][] properties) {
//按照属性0进行排序,从小到大,如果属性0相等,则按属性1进行排序,从大到小
Arrays.sort(properties,(o1,o2)->o1[0]==o2[0]?(o2[1]-o1[1]):(o1[0]-o2[0]));
int ans = 0;
Stack<Integer> stack = new Stack<>();
for(int i = 0;i < properties.length;i++){
// 当前角色的属性1比之前的角色的属性1大,之前的角色为弱角色,出栈,并统计
while(!stack.isEmpty() && stack.peek() < properties[i][1]){
stack.pop();
ans++;
}
// 当前角色入栈
stack.push(properties[i][1]);
}
return ans;
}
方法二:按照某一个属性排序,遍历统计数量。
public int numberOfWeakCharacters(int[][] properties) {
// 属性0从大到小排序,如果属性0相同,属性1从小到大排序
Arrays.sort(properties,(o1,o2)->o1[0]==o2[0]?(o1[1]-o2[1]):(o2[0]-o1[0]));
int max = 0;
int ans = 0;
for(int i = 0;i < properties.length;i++){
// max为之前的最大属性1的值
if(max > properties[i][1]){
// 如果比max小证明该角色为弱角色。
ans++;
}else{
// 更新max值
max = properties[i][1];
}
}
return ans;
}
给你一个大小为 m x n 的整数矩阵 isWater ,它代表了一个由陆地和水域单元格组成的地图。
你需要按照如下规则给每个单元格安排高度:
找到一种安排高度的方案,使得矩阵中的最高高度值最大。
请你返回一个大小为 m x n 的整数矩阵 height ,其中 height[i][j] 是格子 (i, j) 的高度。如果有多种解法,请返回任意一个 。
输入:isWater = [[0,0,1],[1,0,0],[0,0,0]]
输出:[[1,1,0],[0,1,1],[1,2,2]]
解释:所有安排方案中,最高可行高度为 2 。
任意安排方案中,只要最高高度为 2 且符合上述规则的,都为可行方案。
提示:
解题思路:BFS,从水域的点出发,向四周扩散至整个矩阵。在给定的整数矩阵上进行高度修改。
public int[][] highestPeak(int[][] isWater) {
int row = isWater.length;
int col = isWater[0].length;
// 标记是否访问过该点
boolean[][] vised = new boolean[row][col];
// 队列
Queue<int[]> queue = new ArrayDeque<>();
// 将所有的水域节点入队列
for(int i = 0;i < row;i++){
for(int j = 0;j < col;j++){
if(isWater[i][j] == 1){
// 原地修改,水域节点高度为0
isWater[i][j] = 0;
// 将节点坐标加入队列
queue.add(new int[]{i,j});
// 访问标志设为true
vised[i][j] = true;
}
}
}
// 扩散位置数组
int[][] dir = new int[][]{{0,1},{0,-1},{1,0},{-1,0}};
while(!queue.isEmpty()){
// 从队列中的所有节点向外扩散
int size = queue.size();
for(int i = 0;i < size;i++){
int[] tmp = queue.poll();
// 向四个方向扩散
for(int di = 0;di < 4;di++){
// 判断是否越界
if(tmp[0]+dir[di][0] < 0 || tmp[0]+dir[di][0] >= row ||
tmp[1]+dir[di][1] < 0 || tmp[1]+dir[di][1] >= col){
continue;
}
// 判断是否访问过
if(vised[tmp[0] + dir[di][0]][tmp[1] + dir[di][1]] == true){
continue;
}
vised[tmp[0] + dir[di][0]][tmp[1] + dir[di][1]] = true;
// 设置高度
isWater[tmp[0] + dir[di][0]][tmp[1] + dir[di][1]] =
isWater[tmp[0]][tmp[1]] + 1;
// 加入队列
queue.add(new int[]{tmp[0] + dir[di][0],tmp[1] + dir[di][1]});
}
}
}
return isWater;
}
句子是一串由空格分隔的单词。每个单词仅由小写字母组成。
如果某个单词在其中一个句子中恰好出现一次,在另一个句子中却 没有出现 ,那么这个单词就是 不常见的 。
给你两个句子s1和s2,返回所有不常用单词的列表。返回列表中单词可以按任意顺序组织。
示例:
输入:s1 = "this apple is sweet", s2 = "this apple is sour"
输出:["sweet","sour"]
解题思路:题目要求找出在 s1与s2中出现一次的单词,将s1与s2合并,根据空格划分单词,用哈希表统计单词出现次数,次数为1的,为要找的单词,返回即可。
public String[] uncommonFromSentences(String s1, String s2) {
String s = s1 +" "+ s2;
HashMap<String,Integer> hashMap = new HashMap<>();
// 根据空格划分单词
String[] strings = s.split(" ");
// 哈希表统计单词出现的次数
for(int i = 0;i<strings.length;i++){
hashMap.put(strings[i],hashMap.getOrDefault(strings[i],0)+1);
}
Set<Map.Entry<String, Integer>> entries = hashMap.entrySet();
List<String> ans = new ArrayList<>();
for(Map.Entry<String,Integer> entry:entries){
// 将次数为1的单词,加入结果集合
if(entry.getValue() == 1){
ans.add(entry.getKey());
}
}
// 返回结果
return ans.toArray(new String[ans.size()]);
}
给你一个非负整数 num ,请你返回将它变成 0 所需要的步数。 如果当前数字是偶数,你需要把它除以 2 ;否则,减去 1 。
示例:
输入:num = 14
输出:6
解释:
步骤 1) 14 是偶数,除以 2 得到 7 。
步骤 2) 7 是奇数,减 1 得到 6 。
步骤 3) 6 是偶数,除以 2 得到 3 。
步骤 4) 3 是奇数,减 1 得到 2 。
步骤 5) 2 是偶数,除以 2 得到 1 。
步骤 6) 1 是奇数,减 1 得到 0 。
解题思路:模拟过程,步骤计数。
public int numberOfSteps(int num) {
int ans = 0;
while(num != 0){
ans++;
if(num % 2 == 0){
num = num/2;
}else{
num--;
}
}
return ans;
}
当一个字符串 s 包含的每一种字母的大写和小写形式 同时 出现在 s 中,就称这个字符串 s 是美好字符串。比方说,“abABB” 是美好字符串,因为 ‘A’ 和 ‘a’ 同时出现了,且 ‘B’ 和 ‘b’ 也同时出现了。然而,“abA” 不是美好字符串因为 ‘b’ 出现了,而 ‘B’ 没有出现。
给你一个字符串 s ,请你返回 s 最长的美好子字符串 。如果有多个答案,请你返回最早出现的一个。如果不存在美好子字符串,请你返回一个空字符串。
示例 :
输入:s = "YazaAay"
输出:"aAa"
解释:"aAa"是一个美好字符串,因为这个子串中仅含一种字母,其小写形式'a'和
大写形式'A'也同时出现了。"aAa"是最长的美好子字符串。
提示:
解题思路:分治,在没有同时出现的小写字母和大写字母的字母中,分治处理,直到找到美好字符串,并记录字符串的起始位置和长度,找到最长的一个美好子字符串。
class Solution {
// 记录最长字符串的位置和长度
private int maxPos;
private int maxLen;
public String longestNiceSubstring(String s) {
this.maxPos = 0;
this.maxLen = 0;
// 分治处理
dfs(s, 0, s.length() - 1);
return s.substring(maxPos, maxPos + maxLen);
}
public void dfs(String s, int start, int end) {
if(start >= end){
return;
}
// 记录出现的大写字母和小写字母
int lower = 0, upper = 0;
for(int i = start; i <= end; ++i){
if(s.charAt(i) - 'a' >= 0){
// 位运算,使用第i为记录出现过第i个字母,第i位为1,出现了第i位字母。
lower |= 1 << (s.charAt(i) - 'a');
}else{
// 记录出现过的大写字母
upper |= 1 << (s.charAt(i) - 'A');
}
}
// 如果相等,证明大写字母和小写字母同时出现,为最美字符串
if(lower == upper){
if(end - start + 1 > maxLen){
maxPos = start;
maxLen = end - start + 1;
}
return;
}
// 记录出现了大写和小写的字母
int valid = lower & upper;
int pos = start;
while(pos <= end){
// 记录字符串的起始位置
start = pos;
// 找到没有大写小写同时出现的字母的位置,做分治
while(pos <= end && (valid & (1 << Character.toLowerCase(s.charAt(pos)) - 'a')) != 0){
++pos;
}
// pos为没有同时出现大写小写字母的位置,以pos做分治处理
dfs(s, start, pos - 1);
++pos;
}
}
}
给你一个下标从 0 开始的字符串 word 和一个字符 ch 。找出 ch 第一次出现的下标 i ,反转 word 中从下标 0 开始、直到下标 i 结束(含下标 i )的那段字符。如果 word 中不存在字符 ch ,则无需进行任何操作。
示例 :
输入:word = "abcdefd", ch = "d"
输出:"dcbaefd"
解释:"d" 第一次出现在下标 3 。
反转从下标 0 到下标 3(含下标 3)的这段字符,结果字符串是 "dcbaefd" 。
解题思路:找到字符位置,直接反转。
public String reversePrefix(String word, char ch) {
int index = word.indexOf(ch);
if(index >= 0){
int i = 0;
char[] chars = word.toCharArray();
while(i<index){
char t = chars[i];
chars[i] = chars[index];
chars[index] = t;
i++;
index--;
}
word = new String(chars);
}
return word;
}
给你数字 k ,请你返回和为 k 的斐波那契数字的最少数目,其中,每个斐波那契数字都可以被使用多次。
斐波那契数字定义为:
数据保证对于给定的k,一定能找到可行解。
示例:
输入:k = 7
输出:2
解释:斐波那契数字为:1,1,2,3,5,8,13,……
对于 k = 7 ,我们可以得到 2 + 5 = 7 。
提示:
解题思路:贪心思路,k 每次减去最大的斐波那契数字,直到 k 为0为止,记录下次数。
public int findMinFibonacciNumbers(int k) {
int ans = 0;
int a = 1,b = 1,c = 2;
// 找到自身最大的斐波那契数列中的最大数字
while(c <= k){
a = b;
b = c;
c = a + b;
}
// 贪心思想
while(k != 0){
c = b;
b = a;
a = c - b;
// 每次减去最大的数字
if(k >= c){
k = k - c;
ans++;
}
}
return ans;
}
给你一个数组 rectangles ,其中 rectangles[i] = [li, wi] 表示第 i 个矩形的长度为 li 、宽度为 wi 。
如果存在 k 同时满足 k <= li 和 k <= wi ,就可以将第 i 个矩形切成边长为 k 的正方形。例如,矩形 [4,6] 可以切成边长最大为 4 的正方形。
设 maxLen 为可以从矩形数组 rectangles 切分得到的最大正方形的边长。
请你统计有多少个矩形能够切出边长为 maxLen 的正方形,并返回矩形数目 。
示例:
输入:rectangles = [[5,8],[3,9],[5,12],[16,5]]
输出:3
解释:能从每个矩形中切出的最大正方形边长分别是[5,3,5,5]。
最大正方形的边长为5,可以由3个矩形切分得到。
解题思路:一次遍历,找到最大的正方形和对应的次数,返回最大正方形的次数。
public int countGoodRectangles(int[][] rectangles) {
int maxLen = 0;
int ans = 0;
for(int[] rectangle:rectangles){
int min = Math.min(rectangle[0],rectangle[1]);
if(maxLen == min){
ans++;
}else if(maxLen < min){
maxLen = min;
ans = 1;
}
}
return ans;
}