简单题我重拳出击,复杂题我唯唯诺诺!!!
力扣344题:反转字符串
思路 :
思路很简单,就是定义两个指针,前后依次交换数组中的元素,之后头指针++,尾指针- -,直到不满足循环条件退出循环
代码如下:
class Solution {
public void reverseString(char[] s) {
int i = 0, j = s.length - 1;
while(i < j) {
char temp = s[i];
s[i] = s[j];
s[j] = temp;
i++;
j--;
}
}
}
做完上边这个题之后,我们再看下边这个!!!
力扣541题:反转字符串 II
思路如下:
首先需要把字符串变成一个字符数组,之后对字符数组进行操作。每2k个元素操作一次,假如要操作的元素不足k个,我们直接对这些所有的元素进行操作,遍历完之后,返回一个新的字符串。
代码如下:
二刷自己手写版:
class Solution {
public String reverseStr(String s, int k) {
char[] arr = s.toCharArray();
for(int i = 0; i < arr.length; i += 2*k) {
if(i + k < arr.length) {
reverseSubStr(arr, i, i+k-1);
} else {
reverseSubStr(arr, i, arr.length-1);
}
}
// StringBuffer sb = new StringBuffer();
// for(char c : arr) {
// sb.append(c);
// }
// return sb.toString();
return new String(arr);
}
public void reverseSubStr(char[] arr, int start, int end) {
while(start < end) {
char temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
start++;
end--;
}
}
}
一刷参考别人版:
class Solution {
public String reverseStr(String s, int k) {
char[] arr = s.toCharArray();
for(int i = 0; i < arr.length; i += 2 * k) {
int start = i;
//1. 可以用Math函数比大小,选用
// int end = Math.min(arr.length - 1, start + k - 1);
//2. 也可以用三元运算来确定末尾指针的位置
int end = arr.length-1 < start+k-1 ? arr.length-1 : start+k-1;
while(start < end) {
char temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
start++;
end--;
}
}
return new String(arr);
}
}
假期第一天,就做了这两道题,哈哈哈,属实是划水摸鱼了,以后慢慢提上效率来了得,奥里给给给!!!
今天的第一道题就是替换空格呀,难倒是不是很难,就是我不会呀,哈哈哈。问题不大,看看题解,哦!!!原来是这么做呀,码起来======》
剑指Offer第5题:替换空格
思路:
首先得拿到所给字符串的长度吧,拿到长度之后需要创建一个3倍长度的字符数组(为什么呢?由题可知,人家不是要求我们把空格替换成%20嘛,一变三,尽管不一定全用的到,最起码有必要啊),之后遍历字符串的每一个字符,判断是否为空,若是,依次替换,size加3;若不是,把该字符拿过来(拿来吧你!!!)size加1。注意:这里边的size指的是最后新字符串的长度,最后返回一个新字符串(拷贝数组arr从0到size)。
代码如下:
自己二刷代码:
class Solution {
public String replaceSpace(String s) {
StringBuffer sb = new StringBuffer();
char[] arr = s.toCharArray();
for(char c : arr) {
if(c == ' ') {
sb.append("%20");
} else {
sb.append(c);
}
}
return sb.toString();
}
}
一刷参考别人代码:
class Solution {
public String replaceSpace(String s) {
int length = s.length();
char[] arr = new char[length * 3];
int size = 0;
for(int i = 0; i < length; i++) {
char c = s.charAt(i);
if(c == ' ') {
arr[size++] = '%';
arr[size++] = '2';
arr[size++] = '0';
} else {
arr[size++] = c;
}
}
return new String(arr, 0, size);
}
}
看完别人的题解之后,我感觉我又行了,哈哈哈(菜鸡自我感觉良好,呜呜呜)
紧接着又做了第二个题,不做还好,一做又掉了几根头发,可谓是难呀!哈哈哈!!!
总结:二刷自己写的在空间上节约点,时间上二者差不多。
今天从醒来打开电脑就开始帮别人处理数据了,你敢信?每个人的数据都得有一个专属的配套处理方式,哎呀,尽管是东拼西凑出来的代码,那也得会拼凑啊,这就好比是一个人,每个人都有自己适合的穿衣风格,专属发型,呵呵呵,闲话不说,直接进入正题。
今日第一题就碰壁了,理解起来不复杂,需要对一些方法有所了解。
力扣151题:颠倒字符串中的单词
解题思路:
第一步===》去除所有空格(包括字符串前、后以及中间)
第二步===》反转整个字符串(前后对应对调)
第三步===》反转每个单词(从左往右遍历,以空格为单词区分,进行翻转)
代码如下:
class Solution {
public String reverseWords(String s) {
StringBuilder sb = removeSpace(s);
reverse(sb, 0, sb.length()-1);
reverseEachWord(sb);
return sb.toString();
//反转每个单词
public void reverseEachWord(StringBuilder sb) {
int begin = 0, end = 0;
while(begin < sb.length()) {
while(end < sb.length() && sb.charAt(end) != ' ') {
end++;
}
reverse(sb, begin, end - 1);
begin = end + 1;
end++;
}
}
//反转字符串
public void reverse(StringBuilder sb, int left, int right) {
while(left < right) {
char temp = sb.charAt(left);
sb.setCharAt(left, sb.charAt(right));
sb.setCharAt(right, temp);
left++;
right--;
}
}
//清空空格
public StringBuilder removeSpace(String s) {
int left = 0, right = s.length()-1;
//清除字符串左边的所有空格
while(left <= right && s.charAt(left) == ' ') {
left++;
}
//清除字符串右边的所有空格
while(left <= right && s.charAt(right) == ' ') {
right++;
}
//清除中间部分的空格
StringBuilder sb = new StringBuilder();
while(left <= right) {
char c = s.charAt(left);
if(c != ' ' || sb.charAt(sb.length() - 1) != ' ') {
sb.append(c);
}
left++;
}
return sb;
}
}
}
public void setCharAt(int pos, char c);
参数:
int pos – represents the position where we want to set the given character.
int pos –表示我们要设置给定字符的位置。
char c – represents the new character which we want to place.
char c –表示我们要放置的新字符。
不得不说,只要有一个单词打错,你真的是找不到啊!我这个题就因为reverse(sb, 0, sb.length()-1);中的sb写成s,他错误还在reverse函数本身,哈哈哈,真是鸡肋呀!
剑指 Offer 58 - II. :左旋转字符串
解题思路:
我怎么感觉这个题是一个智力题,哈哈!
分为三步===>第一步:翻转0~n-1之间的字符串
第二步:翻转n~末尾的字符串
第三步:全部翻转
代码如下:
class Solution {
public String reverseLeftWords(String s, int n) {
StringBuilder sb = new StringBuilder(s);
reverse(sb, 0, n-1);
reverse(sb, n, s.length()-1);
reverse(sb, 0, s.length()-1);
return sb.toString();
}
public StringBuilder reverse(StringBuilder sb, int start, int end) {
while(start < end) {
char temp = sb.charAt(start);
sb.setCharAt(start, sb.charAt(end));
sb.setCharAt(end, temp);
start++;
end--;
}
return sb;
}
}
力扣165题:比较版本号
解题思路:
首先,以 点 为分隔符,取出来转成数字,比较大小,最后返回相应的数字即可。
代码如下:
class Solution {
public int compareVersion(String version1, String version2) {
int i = 0, j = 0;
//version1,version2 只要有一个没遍历完就接着遍历
while(i < version1.length() || j < version2.length()) {
int x = 0, y = 0;
//对于version1来说,以'.'为分隔,依次取出字符串
while(i < version1.length() && version1.charAt(i) != '.') {
//字符串 转 数字 比大小
x = x*10 + version1.charAt(i) - '0';
i++;
}
while(j < version2.length() && version2.charAt(j) != '.') {
y = y*10 + version2.charAt(j) - '0';
j++;
}
//比完之后返回相应的数
if(x > y) {
return 1;
} else if(x < y) {
return -1;
}
i++;
j++;
}
return 0;
}
}
今天除夕夜,力扣人respect!!!
在这里祝愿所有好厚米新年快乐,万事如意,都能如愿以偿。
力扣929题:独特的电子邮件地址
解题思路:
首先我们要将email字符串进行分割,以@为标志,前边为本地名local,后边为domain域名,域名不用判断,".“和”+“在域名中没有那样的规则;我们着重去处理本地名,先把”+“之后的省略掉,之后将所有的”."都替换成空,此处可以用String类的replace方法和replaceAll方法,参见文献1.
代码如下:
class Solution {
public int numUniqueEmails(String[] emails) {
//因为只放一个值,用Set和List足矣,但是我们这里要求不能重复,所有只能选用Set
Set<String> hashset = new HashSet<>();
//增强for
for(String email : emails) {
//找出@索引并且取出前后作为local和domain
int index = email.indexOf('@');
String local = email.substring(0, index);
String domain = email.substring(index);
//判断local中是否包含+,有的话只要前边的
if(local.contains("+")) {
local = local.substring(0, local.indexOf('+'));
}
//替换 .
// local = local.replaceAll("\\.", "");
local = local.replace(".", "");
//往hashset添加
hashset.add(local + domain);
}
return hashset.size();
}
}
力扣5题:最长回文子串
解题思路:
从左至右依次将字符作为回文字符串的中间字符,如果s是单数,则从中间的一个开始向左向右开始遍历;如果是双数,则从中间的两个开始分别从左从右开始遍历。
代码如下:
class Solution {
public String longestPalindrome(String s) {
String res = "";
for(int i = 0; i < s.length(); i++) {
int j, k;
//s长度为奇数
for(j = i, k = i; j >= 0 && k < s.length() && s.charAt(j) == s.charAt(k); j--, k++) {
if(res.length() < k-j+1) {
res = s.substring(j, k+1);
}
}
//s长度为偶数
for(j = i, k = i+1; j >= 0 && k < s.length() && s.charAt(j) == s.charAt(k); j--, k++) {
if(res.length() < k-j+1) {
res = s.substring(j, k+1);
}
}
}
return res;
}
}
参考文献:
1.replace java_Java中String类下的replace和replaceAll方法的区别
力扣38题:外观数列
思路:先定义一个基本字符串,从这个字符串开始,依次递推,逐层遍历,在数重复字符的时候,通过双指针可以数出来重复的个数,再进行转换字符串和字符串拼接的操作。
代码如下:
class Solution {
public String countAndSay(int n) {
String s = "1";
for(int i = 0; i < n - 1; i++) {
// for(int i = 2; i <= n; ++i) {
//这里不能用String来解题了,Java中的String是定长的,定义一个StringBuilder对象
StringBuilder sb = new StringBuilder();
// String ns = null;
for(int j = 0; j < s.length(); j++) {
int k = j;
while(k < s.length() && s.charAt(k) == s.charAt(j)) {
k++;
}
sb.append(Integer.toString(k - j)).append(s.charAt(j));
// ns += toString(k - j) + s.charAt(j);
//这里可不是j = k 呀,因为for循环里还有个j++操纵,操作完才是j = k
j = k - 1;
}
s = sb.toString();
}
return s;
}
}
这个体中注意的是不能用String来解题了,Java中的String是定长的,定义一个StringBuilder对象这点很重要!!!
力扣49题:字母异位词分组
同样是思想很简单,但是呢,考察的集合知识很麻烦,同样是看了别人的题解,发现自己有个API没见过,不过不要慌,查查文档就找到了,拿下!!!
解题思路:
字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。先得知道什么是字母异位词,找到突破口,那我们就给每个字符串排下序,如果排序之后的字符串相同,那说明属于一类呗!把每一类作为map的键key,之后值用一个List存放字符串,最后返回List内容就完事了呗!
代码如下:
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
Map<String, List<String>> map = new HashMap<>();
for(String str : strs) {
char[] arr = str.toCharArray();
Arrays.sort(arr);
String key = new String(arr);
List<String> list = map.getOrDefault(key, new ArrayList<String>());
list.add(str);
map.put(key, list);
}
return new ArrayList<List<String>>(map.values());
}
}
不会还得多看看别人的题解啊,自己埋头苦干犹如坐进观天,别说浪费时间了,心态都给你搞崩喽!
离开力扣两三天,手就痒痒啊!完了,彻底中毒了,哈哈哈!!!
力扣第3题:无重复字符的最长子串
解题思路:
首先肯定想到的是暴力解法,双指针,里外循环一起走,但是这个时间复杂度为O(n2),为了减少时间复杂度,我们让内循环(左指针)不回头的遍历,拿到一个ans的字符子串长度,丢弃左指针的字符,右指针判断下一个位置的字符是否与现在的字符子串内的字符重复,不重复就加进来,重复就不加,左指针继续向右走。直到越界。
代码如下:
class Solution {
public int lengthOfLongestSubstring(String s) {
//储存不重复的字符
Set<Character> hashset = new HashSet<>();
//将右指针至于左边界的左端
int right = -1;
int ans = 0;
//遍历左指针,记录最长字符子串。扔掉最左边的一个字符,右指针往右走看看是否比上一组字符子串长,长就记录下来
for(int left = 0; left < s.length(); left++) {
if(left != 0) {
hashset.remove(s.charAt(left - 1));
}
while(right+1 < s.length() && !hashset.contains(s.charAt(right + 1))) {
hashset.add(s.charAt(right + 1));
right++;
}
ans = Math.max(ans, right - left + 1);
}
return ans;
}
}
完蛋,晚上遇到一个题,看了别人题解没想明白,哈哈哈,现在脑袋都大了,明天再看看吧,呜呜呜!!!
力扣28题:实现 strStr()
解题思路:
摊牌了,不装了,这道题我选择不理解,就是背,哈哈哈!一些理解在代码注释中
代码如下:
class Solution {
//获取next数组的方法
public void getNext(int[] next, String s) {
//第一步:初始化
int j = -1;
next[0] = j;
//遍历模式串s
for(int i = 1; i < s.length(); i++) {
//第二步:处理前后缀不相同的情况
while(j >= 0 && s.charAt(i) != s.charAt(j+1)) {
j = next[j];
}
//第三步:处理前后缀相同的情况
if(s.charAt(i) == s.charAt(j+1)) {
j++;
}
next[i] = j;
}
}
public int strStr(String haystack, String needle) {
//特判
if(needle.length() == 0) {
return 0;
}
//定义一个大小为needle字符串长的一个整数数组next
int[] next = new int[needle.length()];
//更新next数组
getNext(next, needle);
//定义模式串needle的起始位置,从-1开始,因为next数组就是从-1开始的
int j = -1;
//遍历文本串haystack,从0开始
for(int i = 0; i < haystack.length(); i++) {
//文本串和模式串不等时怎么处理?
while(j >= 0 && haystack.charAt(i) != needle.charAt(j+1)) {
j = next[j];
}
//文本串和模式串相等时怎么处理?
if(haystack.charAt(i) == needle.charAt(j+1)) {
j++;
}
//当j跑到了模式串的末尾,说明文本串里出现了模式串,因为题中要求返回出现的起始位置,所以返回i - needle.length() + 1
if(j == needle.length() - 1) {
return (i - needle.length() + 1);
}
}
//如果跑完都没出现,说明就没有,返回-1
return -1;
}
}
同样遇到另一道题,思路大致相同,只是最后处理有一些不同,大同小异!
力扣459题:重复的子字符串
解题思路:
感觉把KMP算法如何更新next数组的步骤记住就可以做题了,同28题,我这里写了两种方式
代码如下:
class Solution {
//方法一,借鉴28题
public void getNext(int[] next, String s) {
//初始化
int j = -1;
next[0] = j;
for(int i = 1; i < s.length(); i++) {
//第二步:处理前后缀不相同的情况
while(j >= 0 && s.charAt(i) != s.charAt(j+1)) {
j = next[j];
}
//第三步:处理前后缀相同的情况
if(s.charAt(i) == s.charAt(j+1)) {
j++;
}
next[i] = j;
}
}
public boolean repeatedSubstringPattern(String s) {
if(s.length() == 0) {
return false;
}
int[] next = new int[s.length()];
getNext(next, s);
if(next[s.length()-1] != -1 && s.length() % (s.length() - (next[s.length()-1] + 1)) == 0) {
return true;
}
return false;
//方法二
if(s.length() == 0) {
return false;
}
int length = s.length();
// 原串加个空格(哨兵),使下标从1开始,这样j从0开始,也不用初始化了
s = " " + s;
int[] next = new int[length + 1];
// 构造 next 数组过程,j从0开始(空格),i从2开始
for(int i = 2, j = 0; i < length + 1; i++) {
while(j > 0 && s.charAt(i) != s.charAt(j + 1)) {
j = next[j];
}
if(s.charAt(i) == s.charAt(j+1)) {
j++;
}
next[i] = j;
}
if(next[length] > 0 && length % (length - next[length]) == 0) {
return true;
}
return false;
}
}
不得不说,这题真不是给我这种凡夫俗子做的,哈哈哈,都是大佬!!!
到今天为止,字符串就先告一段落了,明天开始“双指针” 的学习,奥力给!!!