目录

344题:反转字符串

7题:颠倒整数

387题:字符串中的第一个唯一字符

242题:有效的字母异位词

125题:验证回文串

8题:字符串转整数 (atoi)

28题:实现strStr()

38题:报数

14题:最长公共前缀


此文章整理于2020年校招期间,当时刷了一部分Leetcode的算法题,拿到了一些大中厂的offer。字符串9道经典算法题的解题思路及代码整理如下:

  • 344题:反转字符串

题目内容:

Leetcode字符串经典九道算法题(代码及解题思路)_算法

解题思路一:刚开始想着拼接字符串的方式,反向读取s的每个字符,然后赋值给新的字符串,结果时间复杂度太高,通过不了。故而采用了将其转为stringbuilder,调用reverse的API。要提前判断为空的情况。

解题思路二:别人的方法:利用toCharArray将其转为char类型数组,重新赋值建立新数组,反向遍历赋值。而且时间复杂度更低。

代码:

// 1.刚开始想着拼接字符串的方式,反向读取s的每个字符,然后赋值给新的字符串,结果时间复杂度太高,通过不了,故而采用
// 一定要提前判断,会减少运算量,直接用stringbuilder中的reverse API。
// public static String reverseString(String s) {
// if (s.length() == 0) {
// return "";
// }
// StringBuffer sb_01 = new StringBuffer(s);
// sb_01.reverse();
// return sb_01.toString();
// }
//2.将其转为char类型数组,利用重新赋值建立新数组,反向读取赋值,也可以搞定。而且时间复杂度更低。
public static String reverseString(String s) {
char[] chars = s.toCharArray();
char[] newString = new char[chars.length];
for (int i = chars.length - 1, t = 0; i >= 0; i--, t++) {
newString[t] = chars[i];
}
return new String(newString);
}
  • 7题:颠倒整数

题目内容:

Leetcode字符串经典九道算法题(代码及解题思路)_字符串_02

解题思路:这个题当时还挺麻烦的,注重细节太多。因为StringBuilder和StringBuffer的对象是变量,对变量进行操作就是直接对该对象进行更改,而不进行创建和回收的操作。故而使用StringBuilder来搞定。步骤如下:(1)判断数字是否大于零,若小于零,则从下标为1开始截取字符串;(2)使用result +=(Math.pow(10,i)*tmp);tmp为正向读取到的每个元素;result为long类型。(3)判断其是否大于Integer.MAX_VALUE或者小于Integer.MIN_VALUE,返回相应的值。注意以下几点:(1)MAth.abs不能用;(2)Integer.MAX_VALUE判断最大值;(3)判断反转之后的有没有越界,而不是之前的有没有越界。

代码:

//StringBuilder和StringBuffer的对象是变量,对变量进行操作就是直接对该对象进行更改,而不进行创建和回收的操作,所以速度要比String快很多。
//md一定要注意越界问题,(1)MAth.abs不能用。(2)Integer.MAX_VALUE判断最大值;(3)判断反转之后的有没有越界,而不是之前的有没有越界。
public static int reverse(int x) {
StringBuilder sb_01;
long result = 0;
if (x >= 0) {
sb_01 = new StringBuilder(String.valueOf(x));
} else {
sb_01 = new StringBuilder(String.valueOf(x).substring(1, String.valueOf(x).length()));
}
for(int i = 0; i<sb_01.length();i++){
int tmp = Integer.parseInt(sb_01.substring(i,i+1));
result +=(Math.pow(10,i)*tmp);
}
if(result<Integer.MIN_VALUE || result>Integer.MAX_VALUE){
result = 0;
}
return x>0?(int)result:-(int)result;
}
  • 387题:字符串中的第一个唯一字符

题目内容:

Leetcode字符串经典九道算法题(代码及解题思路)_字符串_03

解题思路一:(1)双重循环遍历并更改字符串:遍历一遍将重复元素都置为'A'.若有改变则将flag置为true,将new_str[i]最后也置为'A'。(2)循环遍历字符串,查找第一个不等于‘A’的元素。注意:若被比较元素为‘A’,则不对其进行比较了,直接下一个,能加快效率。

解题思路二:别人的方法:比我的方法快十倍,思路比较清奇,主要是遍历a-z的元素,记录其第一次出现的位置和最后出现一次的位置是否相同,若相同,则说明只出现了一次,记录该点位置,有更小的位置则对其不断进行更新。若不同,则不用处理。最后判定如果全部相等的话就将其置为-1。

代码:

public static int firstUniqChar(String s) {
char[] new_str = s.toCharArray();
boolean flag = false;
//遍历并更改字符串。第一尽量减少复杂度;第二看清楚条件,若没有结果输出-1,而不是0.谢谢。
if (new_str.length != 0) {
for (int i = 0; i < s.length() - 1; i++) {
if (new_str[i] != 'A') {
for (int j = i + 1; j < s.length(); j++) {
//遍历一遍将重复元素都置为'A'.若有改变则将flag置为true,提示将new_str[i]最后也置为'A'
if (new_str[i] == new_str[j]) {
flag = true;
new_str[j] = 'A';
}
}
if (flag) {
new_str[i] = 'A';
}
flag = false;
}
}
int result = -1;
for (int i = 0; i < s.length(); i++) {
if (new_str[i] != 'A') {
result = i;
break;
}
}
return result;
} else {
return -1;
}
}
//比我的时间加快十倍,思路比较清奇,主要是遍历a-z的元素,记录其第一次出现的位置和最后出现一次的位置是否相同,
// 若相同,则说明只出现了一次,记录该点位置。并对其不断进行更新,若不同,则不用处理。最后判定如果全部相等的话就将其置为-1。
// public static int firstUniqChar(String s) {
// int index=-1;
// int result=s.length();
// for(char ch='a';ch<='z';ch++){
// index=s.indexOf(ch);
// if (index!=-1&&index==s.lastIndexOf(ch)){
// result = result>index?index:result;
// }
// }
// return result> s.length()-1?-1:result;
// }
  • 242题:有效的字母异位词

题目内容:

Leetcode字符串经典九道算法题(代码及解题思路)_leetcode_04

解题思路一:(1)字符串转数组;2.利用API对数组排序;3.对比数组的每一个元素,判断是否相等。

解题思路二:别人的方法:比我的快5倍。(1)建立两个数组,用于存放26个字符中每个元素出现的次数;(2)进行遍历,若a出现了5次,则sArr[1]=5;(3)判断两个数组每个位置是否相等。

代码:

//(1)字符串转数组;2.利用API对数组排序;3.对比数组的每一个元素,判断是否相等。
// public static boolean isAnagram(String s, String t) {
// if (s.length() == t.length()) {
// char[] arrays_01 = s.toCharArray();
// char[] arrays_02 = t.toCharArray();
// Arrays.sort(arrays_01);
// Arrays.sort(arrays_02);
// for(int i = 0;i<arrays_01.length;i++){
// if(arrays_01[i] != arrays_02[i]){
// return false;
// }
// }
// return true;
// } else {
// return false;
// }
// }
//比我的快5倍。(1)建立两个数组,用于存放26个字符中每个元素出现的次数;(2)进行遍历,若a出现了5次,则sArr[1]=5;
//(3)判断两个数组每个位置是否相等。
public static boolean isAnagram(String s, String t) {
int[] sArr = new int[26];
int[] tArr = new int[26];
for(char c : s.toCharArray()){
sArr[c - 'a']++;
}
for(char c : t.toCharArray()){
tArr[c - 'a']++;
}
for(int i=0;i<26;i++){
if(sArr[i] != tArr[i])
return false;
}
return true;

}
  • 125题:验证回文串

题目内容:

Leetcode字符串经典九道算法题(代码及解题思路)_数组_05

解题思路一:(1)建立新Arraylist数组,正向遍历,若为字母和数字,添加进去,形成新数组。(2)再正向遍历一遍,若arrayList_01.get(i)!=arrayList_01.get(arrayList_01.size()-1-i),则return false,只需要遍历下标到arrayList_01.size()/2即可。

解题思路二:别人的方法:不额外使用别的存储空间,利用本身的数组从前面和后面同时遍历,然后利用isLetterOrDigit和toLowerCase两个API来具体计算。当low>=high时停止。

代码:

//    public static boolean isPalindrome(String s) {
// ArrayList <Character>arrayList_01 = new ArrayList<Character>();
// if(s.length()>1) {
// for (int i = 0; i < s.length(); i++) {
// if ((s.charAt(i) >= 'a' && s.charAt(i) <= 'z') || (s.charAt(i) >= 'A' && s.charAt(i) <= 'Z')
// || (s.charAt(i) >= '0' && s.charAt(i) <= '9')) {
// if (s.charAt(i) >= 'A' && s.charAt(i) <= 'Z') {
// arrayList_01.add((char) (s.charAt(i)+32));
// } else {
// arrayList_01.add(s.charAt(i));
// }
// }
// }
// for(int i = 0;i<arrayList_01.size()/2;i++){
// if(arrayList_01.get(i)!=arrayList_01.get(arrayList_01.size()-1-i)){
// return false;
// }
// }
// return true;
// }else{
// return true;
// }
// }
//2.他的方案是不额外使用别的存储空间,利用本身的数组从前面和后面同时遍历,然后利用isLetterOrDigit和
// toLowerCase两个API来具体计算。当low>=high是停止。
public static boolean isPalindrome(String s) {
if(s.isEmpty()){
return true;
}
int head=0,tail=s.length()-1;
char cHead,cTail;
while(head<tail){
cHead=s.charAt(head);
cTail=s.charAt(tail);
if(!Character.isLetterOrDigit(cHead)){
head++;
}
else if(!Character.isLetterOrDigit(cTail)){
tail--;
}
else{
if(Character.toLowerCase(cHead)!=Character.toLowerCase(cTail))
return false;
head++;
tail--;
}

}
return true;
}
  • 8题:字符串转整数 (atoi)

题目内容:

Leetcode字符串经典九道算法题(代码及解题思路)_leetcode_06

难度等级:Medium

解题思路一:Medium的题果然考虑更多,(一)将字符串转为字符数组后,遍历一遍数组,设置标志位并考虑以下五种情况:(1)若其为空格且flag为False时将其下标递增;(2)若其为-号或+号时且flag为False时将其加进数组中去;(3)若数字不在0-9之间,且flag为flase时,则跳出;(4)若在0-9时,将其加进数组中去;(5)若flag为true时,若数字不在0-9,跳出。(二)检查字符串的有效性。字符串长度为零等等;(三)利用try...catch转换失败的异常情况来搞定大于2^31的情况。

解题思路二:别人的方法:比我快两倍,思路特别清晰:充分利用了trim()的API:(1)先判断字符串为空和str.trim长度为零的情况;(2)对trim之后的字符串的第一个字符先判断其+-;(3)然后在逐个读取字符,若字符字符>=0<=9时计算res值;并进行累加;若大于MAx或<min时返回相应的最大最小值;若不是数字则break;(4)最后根据+-输出相应的值,思路太清晰了有木有。

代码:

//    public static int myAtoi(String str) {
// char[] new_str = str.toCharArray();
// boolean flag = false;
// String result = new String();
// 设置标志位,分为四种情况。(1)若其为空格且flag为False时将其下标递增;(2)若其为-号时且flag为False时将其加进数组中去;
// (3)若数字不在0-9之间,且flag为flase时,则跳出;(4)若在时,将其加进数组中去;(5)若flag为true时,若数字不在0-9,跳出
// for (int i = 0; i < new_str.length; i++) {
// if (new_str[i] == 32 && !flag) {
// continue;
// } else if (new_str[i] == 45 && !flag || new_str[i] == 43 && !flag) {
// result += new_str[i];
// flag = true;
// } else if ((new_str[i] < 48 && !flag) || (new_str[i] > 57 && !flag)) {
// break;
// } else if (new_str[i] >= 48 && new_str[i] <= 57) {
// result += new_str[i];
// flag = true;
// } else if (flag) {
// break;
// }
// }
// boolean flag_02 = false;
// if (result.length() != 0) {
// if ((result.charAt(0) != 45 && result.charAt(0) < 48 && result.charAt(0) != 43)
// || result.charAt(0) != 45 && result.charAt(0) != 43 && result.charAt(0) > 57) {
// flag_02 = true;
// }
// //检验字符串的有效性,遍历一遍
// for (int i = 1; i < result.length(); i++) {
// if (result.charAt(i) < 48 && result.charAt(i) > 57) {
// flag_02 = true;
// }
// }
// if (result.charAt(0) == 45 && result.length() == 1 || result.charAt(0) == 43 && result.length() == 1) {
// return 0;
// }
// } else {
// return 0;
// }
// long back_result;
// //这块利用try...catch啊小哥哥,是不是傻。
// try {
// back_result = Long.parseLong(result);
// //输出结果,如果<min_value,如果>max_value.如果正常情况下。
// } catch (Exception ee) {
// if (result.charAt(0) == '-')
// return Integer.MIN_VALUE;
// else
// return Integer.MAX_VALUE;
// }
//
// if (flag_02) {
// return 0;
// } else {
// if (back_result < Integer.MIN_VALUE) {
// return Integer.MIN_VALUE;
// } else if (back_result > Integer.MAX_VALUE) {
// return Integer.MAX_VALUE;
// } else {
// return (int) back_result;
// }
// }
// }
//比我快两倍,思路特别清晰(1)先判断字符串为空和str.trim长度为零的情况;(2)对trim之后的字符串先判断其+-;
//(3)然后在逐个读取字符,若字符字符>=0<=9时计算res值;若大于MAx或<min时返回相应的最大最小值;若不是数字则break;
//(4)最后判断+-,思路太清晰。
public static int myAtoi(String str) {
if(str == null || str.trim().length() == 0) return 0;
long res = 0;
int flag = 1;
int i = 0;
String newstr = str.trim();
if(newstr.charAt(i) == '+') {
flag = 1;
i++;
}else if(newstr.charAt(i) == '-'){
flag = -1;
i++;
}
while(i < newstr.length()){
if(newstr.charAt(i) >= '0' && newstr.charAt(i) <= '9'){
res = res * 10 + (newstr.charAt(i++) - '0');
if(flag == 1 && res > Integer.MAX_VALUE) return Integer.MAX_VALUE;
if(flag == -1 && (flag * res) < Integer.MIN_VALUE) return Integer.MIN_VALUE;
}else{
break;
}
}
return flag == 1 ? (int)res : (int)(flag*res);
}
  • 28题:实现strStr()

题目内容:

Leetcode字符串经典九道算法题(代码及解题思路)_算法_07

解题思路一:(1)判断两个string长度,若第一个小于第二个,则return -1;(2)记录每次第一个相等位置的下标,然后在此基础之上比较,若遍历至第二个字符串最后一个元素也相等,则结束,并return开始的位置,若中间不同,则break跳出;同时要防止第一个字符串在遍历过程中越界问题。(3)判断第二个字符串的长度不为零。第一遍未通过时未考虑到:(1)haystack的长度小于needle的长度;(2)在比较时,haystack获取的位置不能越界。

解题思路二:别人的方法:好简单,利用indexof的API即可解决,同时为了加快执行时间,先考虑needle长度为零和haystack长度为零的情况。

代码:

//未考虑到(1)haystack的长度小于needle的长度;(2)在比较时,haystack获取的位置不能越界。
//解题思路:(1)判断两个string长度,若第一个小于第二个,则return -1;(2)记录每次第一个相等位置的下标,
// 然后在此基础之上比较,若遍历至第二个字符串最后一个元素也相等,则结束,并return开始的位置,若中间不同,则break跳出;
//同时要防止第一个字符串在遍历过程中越界问题。
//(3)判断第二个字符串的长度不为零。
//执行时间:812ms
// public static int strStr(String haystack, String needle) {
// int backresult = -1;
// if (haystack.length() < needle.length()) {
// return -1;
// }
// if (needle.length() != 0) {
// for (int i = 0; i < haystack.length(); i++) {
// int j;
// if (haystack.charAt(i) == needle.charAt(0)) {
// for (j = 0; j < needle.length(); j++) {
// if (i + j < haystack.length()) { //必须不能超过haystack的长度
// if (haystack.charAt(i + j) == needle.charAt(j)) {
// continue;
// } else {
// break;
// }
// }else{
// break;
// }
// }
// if (j == needle.length()) {
// return i;
// }
// }
// }
// return backresult;
// } else {
// return 0;
// }
// }

//利用indexof的API,去计算needle在haystack的位置,同时需要考虑needle长度为零和haystack长度为零的情况。执行时间:6ms
public static int strStr(String haystack, String needle) {
int m = needle.length();//needle字符串的长度
if (m == 0) {
return 0;
} else {
if (haystack.length() == 0) {
return -1;
} else {
//return haystack.substr(needle); 不能直接采用substr函数,substr表示指定输出从何处开始多长的子字符串
return haystack.indexOf(needle);
}
}
}
  • 38题:报数

题目内容:

Leetcode字符串经典九道算法题(代码及解题思路)_面试_08

解题思路一:这个题应该划归到medium里面,光理解题意都得小半会。1.先对字符串Stringbuilder进行分割,设置begin下标位置为0,(1)若字符串中的字符前者不等于后者,则统计begin下标到该处的位置和字符,放进ArrayList中(Arraylist里面存了数字和重复次数),更新begin的值;(2)若到了字符串的结束位置,也将其存储进去。 2.更新Stringbuilder内容,计算时候一定要细心,重复n-1次。

解题思路二:别人的方法:执行时间为2ms,不需要额外的空间。每一个数字内容已经确定,只需要统计他出现的次数。每次append(count).append(cha)可以完美解决,每次更新字符串和新字符,重置统计个数。看代码哈哈哈哈。append()原来可以这么玩。

代码:

public class Main {

public static void main(String[] args) {
// write your code here
int n = 4;
System.out.println(countAndSay(6));
}

// //执行时间2ms;
// //解题思路:不需要额外的空间。每一个数字内容已经确定,只需要统计他出现的次数。每次append(count).append(cha)
// // 可以完美解决,每次更新字符串和新字符,重置统计个数。
// public static String countAndSay(int n) {
// int count = 1;
// String s = "1";
// while (count ++ < n) {
// s = readString(s);
// }
// return s;
// }
//
// private static String readString(String s) {
// char[] chs = s.toCharArray();
// char c = chs[0];
// int count = 1;
// StringBuilder sb = new StringBuilder();
// for (int i = 1; i < chs.length; i++) {
// if (chs[i] == c) {
// count ++;
// } else {
// sb.append(count).append(c);
// c = chs[i];
// count = 1;
// }
// }
//
// sb.append(count).append(c);
// return sb.toString();
// }
//执行用时:9 ms,超过55%。
//1.先对字符串Stringbuilder进行分割,设置begin下标位置为0,若字符串中的字符前者不等于后者,则统计begin下标到该处的位置和字符,放进ArrayList中;
//若到了字符串的结束位置,则将其存储进去(先判断这个)
//2.更新Stringbuilder内容,计算时候一定要细心,重复n-1次。
public static String countAndSay(int n) {
String init_Str = "1";
StringBuilder init_stb = new StringBuilder(init_Str);
for (int j = 0; j < n-1; j++) {
int begin = 0; //记录每个字符串开始的位置。
ArrayList<stru> arrArrayList_01 = new ArrayList<stru>();
//对每个字符串进行分割,分割成相应的ArrayList。
for (int i = 0; i < init_stb.length(); i++) {
if (i == init_stb.length() - 1) {
stru stru_01 = new stru();
stru_01.array_con = init_stb.charAt(i);
stru_01.array_amo = init_stb.length() - begin;
arrArrayList_01.add(stru_01);//将长度和内容存进数组里面。
} else if (init_stb.charAt(i) != init_stb.charAt(i + 1)) {
stru stru_01 = new stru();
stru_01.array_con = init_stb.charAt(i);
stru_01.array_amo = i + 1 - begin;
arrArrayList_01.add(stru_01);//将长度和内容存进数组里面。
begin = i + 1;//更新分割字符串开始的位置。
}
}
//更新旧数组内容。
for (int i = 0; i < arrArrayList_01.size(); i++) {
init_stb.replace(i * 2, i * 2 + 1, String.valueOf(arrArrayList_01.get(i).array_amo));
init_stb.replace(i * 2 + 1, i * 2 + 2, String.valueOf(arrArrayList_01.get(i).array_con));
}
}
return init_stb.toString();
}

}

class stru {
char array_con;//内容
int array_amo;//数量
}
  • 14题:最长公共前缀

题目内容:

Leetcode字符串经典九道算法题(代码及解题思路)_leetcode_09

解题思路一:(1)先找到最小字符串的长度,然后记录下来;(2)以最小长度为外层循环。以数组长度为内层循环,进行遍历,若该位前者等于后者,则累加字符串,并对比第二个和第三个字符串的第一位。若不等,则break,设置flag,告诉系统结束了;若最后一个字符串等于数组长度,则对比下一位。(第一次提交时未考虑到:若数组长度为空,则返回空字符串;若长度为1,则返回第一个数组的全部字符串)。

解题思路二:别人的方法:执行用时:6 ms。思路比较清奇,充分利用了indexof的API。(1)若字符数组长度为零,则return空;(2)记录数组第一个元素为result;(3)对后面的每个字符串进行遍历,若indexof()不为零,则截取字符串(0,s.length()-1),若为零,则对下一个字符串进行indexof(s)。

代码:

//解决方法:1.先找到最小字符串的长度,然后记录下来;2.以最小长度为外层循环。以数组长度为内层循环,进行遍历,
// 若该位前者等于后者,则累加字符串,并对比第二个和第三个字符串的第一位。若不等,则break,设置flag,告诉系统结束了;如果最后一个字符串
// 等于数组长度,则对比下一位。(第一次提交时未考虑到:若数组长度为空,则返回空字符串;若长度为1,则返回第一个数组的全部字符串
// 执行用时:12 ms
// public static String longestCommonPrefix(String[] strs) {
// if(strs.length == 0) return "";
// if(strs.length == 1) return strs[0];
// String result = new String();
// int min = strs[0].length();
// for (int i = 1; i < strs.length; i++) {
// if (min > strs[i].length()) {
// min = strs[i].length();
// }
// }
// boolean flag = false;
// for (int i = 0; i < min; i++) {
// for(int j = 0;j<strs.length-1;j++){
// if(strs[j].charAt(i) == strs[j+1].charAt(i)){
// if(j==strs.length-2){
// result += strs[j].charAt(i);
// }
// continue;
// }else{
// flag =true;
// break;
// }
// }
// if(flag) break;
// }
// return result;
// }
// 执行用时:6 ms
//1。若字符数组长度为零,则return 空;2.记录第一个数组为result;3.对后面的每个字符串进行遍历,若indexof()
//不为零,则对s-1,遍历数组内所有字符串,若不包含,则--,若包含,则下一个字符串。很清奇的思路。充分利用了indexof的API。
public static String longestCommonPrefix(String[] strs) {
if (strs.length == 0) {
return "";
}
String s = strs[0];
for (int i = 1; i < strs.length; i++) {
while (strs[i].indexOf(s) != 0) {
s = s.substring(0, s.length() - 1);
if (s.isEmpty()) {
return "";
}
}
}
return s;
}