力扣1190/6/剑指Offer57-II/58-I/-II
Posted Zephyr丶J
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了力扣1190/6/剑指Offer57-II/58-I/-II相关的知识,希望对你有一定的参考价值。
1190. 反转每对括号间的子串
2021.5.26每日一题,终于来了个能做的
题目描述
给出一个字符串 s(仅含有小写英文字母和括号)。
请你按照从括号内到外的顺序,逐层反转每对匹配括号中的字符串,并返回最终的结果。
注意,您的结果中 不应 包含任何括号。
示例 1:
输入:s = "(abcd)"
输出:"dcba"
示例 2:
输入:s = "(u(love)i)"
输出:"iloveu"
示例 3:
输入:s = "(ed(et(oc))el)"
输出:"leetcode"
示例 4:
输入:s = "a(bcdefghijkl(mno)p)q"
输出:"apmnolkjihgfedcbq"
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/reverse-substrings-between-each-pair-of-parentheses
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
刚开始想直接用栈,然后想了半天也没想出来怎么处理
然后反转么,就想到了双指针,再结合栈,就写出来了,时间复杂度O(n2)还超了99%,有点诧异
class Solution {
public String reverseParentheses(String s) {
//一看到这种有括号的题,先想到的是栈
//想了一下不行,双指针吧,从两边到中间处理,先转换成数组
//看到左括号,入栈,看到右括号,取出左括号下标进行反转操作
char[] ss = s.toCharArray();
int l = ss.length;
Stack<Integer> stack = new Stack<>(); //记录左下标的位置
for(int i = 0; i < l; i++){
char c = ss[i];
if(c == '('){
stack.push(i);
}else if(c == ')'){
int left = stack.pop();
int right = i;
reverse(ss, left, right);
}
}
StringBuffer sb = new StringBuffer();
for(int i = 0; i < l; i++){
if(ss[i] != '(' && ss[i] != ')'){
sb.append(ss[i]);
}
}
return sb.toString();
}
public void reverse(char[] ss, int left, int right){
//左下标是左括号,右下标是右括号
char[] temp = new char[ss.length];
for(int i = left; i <= right; i++){
temp[i] = ss[i];
}
int index = right - 1;
for(int i = left + 1; i < right; i++){
//如果等于左括号,说明左边处理完了
ss[index--] = temp[i];
}
}
}
看了官解,第一个方法也是遇到括号反转字符串,第二个方法很巧妙。
首先预处理括号,记录每个括号对应的另一半的下标。然后从左到右遍历字符串,每当碰到一个括号时,就找它另一半的下标,然后把遍历方向取反,太妙了
class Solution {
public String reverseParentheses(String s) {
int n = s.length();
//存储括号对
int[] pair = new int[n];
Deque<Integer> stack = new LinkedList<>();
for (int i = 0; i < n; i++) {
if (s.charAt(i) == '(') {
stack.push(i);
} else if (s.charAt(i) == ')') {
int j = stack.pop();
pair[i] = j;
pair[j] = i;
}
}
StringBuffer sb = new StringBuffer();
//刚开始向右遍历
int index = 0, step = 1;
while (index < n) {
//遇到括号就找到对应的括号,并且将遍历方向改变
if (s.charAt(index) == '(' || s.charAt(index) == ')') {
index = pair[index];
step = -step;
//遇到字符就添加到字符串末尾
} else {
sb.append(s.charAt(index));
}
index += step;
}
return sb.toString();
}
}
6. Z 字形变换
题目描述
将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:
P A H N
A P L S I I G
Y I R
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"。
请你实现这个将字符串进行指定行数变换的函数:
string convert(string s, int numRows);
示例 1:
输入:s = "PAYPALISHIRING", numRows = 3
输出:"PAHNAPLSIIGYIR"
示例 2:
输入:s = "PAYPALISHIRING", numRows = 4
输出:"PINALSIGYAHRPI"
解释:
P I N
A L S I G
Y A H R
P I
示例 3:
输入:s = "A", numRows = 1
输出:"A"
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zigzag-conversion
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
做这个题的动机是看到今天每天一题的题解里有人提到了这个题,又是前300道,就过来看看
找规律的题
class Solution {
public String convert(String s, int numRows) {
//感觉像是找规律题一样,找一下规律应该就做出来了
//1 9 17
//2 8 10 16 18
//3 7 11 15 19
//4 6 12 14
//5 13
//5行,所以第一行两个数相差 (5 - 1) * 2 = 8
//第二行,6,2;第三行4,4....
if(numRows == 1)
return s;
int left = (numRows - 1) * 2;
int right = 0;
int l = s.length();
StringBuffer sb = new StringBuffer();
for(int i = 0; i < numRows; i++){
int j = i;
if(j < l)
sb.append(s.charAt(j));
while(j < l){
if(i != numRows - 1){
j += left;
if(j >= l)
break;
sb.append(s.charAt(j));
}
if(i != 0){
j += right;
if(j >= l)
break;
sb.append(s.charAt(j));
}
}
left -= 2;
right += 2;
}
return sb.toString();
}
}
看了第一个题解,才发现和之前那个题有啥联系,做法就是先每一个非空行,创建一个集合,然后遍历字符串,模拟Z字的走法,将遍历到的字符存入对应的集合中,走到头,将遍历方向取反
class Solution {
public String convert(String s, int numRows) {
if(numRows < 2) return s;
List<StringBuilder> rows = new ArrayList<StringBuilder>();
for(int i = 0; i < numRows; i++)
rows.add(new StringBuilder());
int i = 0, flag = -1;
for(char c : s.toCharArray()) {
rows.get(i).append(c);
if(i == 0 || i == numRows -1) flag = - flag;
i += flag;
}
StringBuilder res = new StringBuilder();
for(StringBuilder row : rows) res.append(row);
return res.toString();
}
}
作者:jyd
链接:https://leetcode-cn.com/problems/zigzag-conversion/solution/zzi-xing-bian-huan-by-jyd/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
剑指 Offer 57 - II. 和为s的连续正数序列
题目描述
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
示例 1:
输入:target = 9
输出:[[2,3,4],[4,5]]
示例 2:
输入:target = 15
输出:[[1,2,3,4,5],[4,5,6],[7,8]]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
因为一个等差序列的和可以O(1)内求出来
而要求目标为target的所有正整数序列
那么遍历正整数序列的长度l,求平均值,即target / l,那么这个平均值就是这个序列的中心值,或者中心值偏左一位,与序列长度奇偶性相关
然后根据长度的奇偶确定左右边界的下标,根据求和公式计算序列和,如果等于target就放入集合中,同时如果left减小到1或小于1了,那么说明长度不可能再增大了,直接跳出循环
这里学习一下,怎么将一个存放数组的集合转换成一个二维数组
class Solution {
public int[][] findContinuousSequence(int target) {
List<int[]> list = new ArrayList<>();
//计算等差数列的和
//枚举长度
for(int i = 2; i < target; i++){
int mid = target / i;
int half = i / 2;
int left = i % 2 == 0 ? mid - (half - 1) : mid - half;
int right = mid + half;
if((left + right) * i / 2 == target){
int[] res = new int[right - left + 1];
int index = 0;
for(int j = left; j <= right; j++){
res[index++] = j;
}
list.add(res);
}
if(left <= 1)
break;
}
Collections.reverse(list);
//怎么将一个存放数组的集合转换成一个二维数组
return list.toArray(new int[list.size()][]);
}
}
或者滑动窗口,刚开始左右指针分别指向1和2,因为最小长度为2
然后计算left和right之间的和,如果小于target,那么移动右指针
如果大于target,移动左指针
如果等于,加入集合中,同时移动左指针或者右指针
class Solution {
public int[][] findContinuousSequence(int target) {
List<int[]> list = new ArrayList<>();
//计算等差数列的和
//枚举长度
int left = 1;
int right = 2;
int sum = 3;
while(left < right){
if(sum == target){
int[] res = new int[right - left + 1];
int index = 0;
for(int i = left; i <= right; i++){
res[index++] = i;
}
list.add(res);
sum -= left;
left++;
}else if(sum > target){
sum -= left;
left++;
}else{
right++;
sum += right;
}
}
return list.toArray(new int[0][]);
}
}
剑指 Offer 58 - I. 翻转单词顺序
题目描述
输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. ",则输出"student. a am I"。
示例 1:
输入: "the sky is blue"
输出: "blue is sky the"
示例 2:
输入: " hello world! "
输出: "world! hello"
解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
示例 3:
输入: "a good example"
输出: "example good a"
解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/fan-zhuan-dan-ci-shun-xu-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
class Solution {
public String reverseWords(String s) {
String[] strs = s.trim().split(" ");
StringBuilder sb = new StringBuilder();
for(int i = strs.length - 1; i >= 0; i--){
if(strs[i].equals("")) //注意这里是等于“”,空,而不是空格
continue;
sb.append(strs[i]);
sb.append(" ");
}
return sb.toString().trim();
}
}
class Solution {
public String reverseWords(String s) {
//不调用库函数,从后向前遍历,遍历到一个单词加入
int l = s.length();
if(l == 0)
return s;
int j = l - 1;
while(j >= 0){
if(s.charAt(j) != ' ')
break;
j--;
}
StringBuilder sb = new StringBuilder();
for(int i = j; i >= 0; i--){
if(s.charAt(i) == ' '){
sb.append(s.substring(i + 1, j + 1) + " ");
j = i;
}
while(i >= 0 && s.charAt(i) == ' '){
i--;
j--;
}
}
if(s.charAt(0) != ' ')
sb.append(s.substring(0, j + 1) + " ");
return sb.toString().trim();
}
}
剑指 Offer 58 - II. 左旋转字符串
题目描述
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。
示例 1:
输入: s = "abcdefg", k = 2
输出: "cdefgab"
示例 2:
输入: s = "lrloseumgh", k = 6
输出: "umghlrlose"
思路
书中所给的是反转思路,这里也实现也一下
更简单的思路就是直接截取字符串然后拼接
class Solution {
public String reverseLeftWords(String s, int n) {
int l = s.length();
char[] ss = s.toCharArray();
reverse(0, n - 1, ss);
reverse(n, l - 1, ss);
reverse(0, l - 1, ss);
return new String(ss);
}
public void reverse(int i, int j, char[] ss){
while(i < j){
char temp = ss[i];
ss[i] = ss[j];
ss[j] = temp;
i++;
j--;
}
}
}
以上是关于力扣1190/6/剑指Offer57-II/58-I/-II的主要内容,如果未能解决你的问题,请参考以下文章