字符串总结
28. 实现 strStr()
class Solution {
public int strStr(String haystack, String needle) {
/**
分析:
我的第一想法就是使用滑动窗口解题,事实上确实可以使用滑动窗口。
但是对于字符串匹配问题,一般的思路就是暴力解法和kmp算法
*/
// 使用滑动窗口
int len1 = haystack.length(), len2 = needle.length();
if(len2 == 0){
return 0;
}
// 定义双指针(窗口指针)
int p1 = 0, p2 = len2 - 1;
// 循环遍历结束条件是p2指针达到len1
while( p2 < len1){
// 这里要注意,substring()是左闭右开的
if(haystack.substring(p1,p2+1).equals(needle)){
return p1;
}
p1++;
p2++;
}
return -1;
}
}
38. 外观数列(模拟进行循环,变态的过程)
class Solution {
public String countAndSay(int n) {
/**
分析:
这题能想出来是怪胎把~,使用循环和递归解法。这里使用循环解法。
外层循环是进行层数(1-n),里层循环是判断是否有重复的数字
*/
// 初始值为1
StringBuilder curr = new StringBuilder("1");
// 定义一个临时字符串用来储存上一次的信息
StringBuilder prev;
// 第一层循环,用于迭代1-n,下标从1开始,因为已经初始化了
for(int i = 1; i < n; i++){
// 迭代上一次信息
prev = curr;
// 构造当前信息
curr = new StringBuilder();
// 对上一次信息分析:count和say
int count = 1;
char say =prev.charAt(0);
// 第二层循环用来判断prev是否有重复,有就count++,否则就直接加入curr并且更新say和count
for(int j = 1; j < prev.length(); j++){
// 若是有重复的say
if(prev.charAt(j) == say){
count++;
}else{
// 否则就更新curr,say和count
curr.append(count).append(say);
count = 1;
say = prev.charAt(j);
}
}
// 对最后一次信息进行更新
curr.append(count).append(say);
}
return curr.toString();
}
}
58. 最后一个单词的长度(倒序遍历+双指针)
class Solution {
public int lengthOfLastWord(String s) {
/**
分析:
题目要求最后一个单词的长度,那不就是倒序遍历的第一个单词的长度~~~
*/
int end = s.length() - 1;
// 先去除末尾的空格
while(end >= 0 && s.charAt(end) == ' '){
end--;
}
int start = end;
// 定位start为最后一个单词前的空格位置
while(start >= 0 && s.charAt(start) != ' '){
start--;
}
// 返回距离
return end - start;
}
}
165. 比较版本号(实际上就是比较大小)
注意:这里使用了spilt内置函数,若面试不允许使用内置函数,那么就得用方法二了
class Solution {
public int compareVersion(String version1, String version2) {
/**
分析:
所谓的比较版本号,实际上就是在比较大小。这里数字越靠前的,权重越大,那么完全可以将其权重化。
有两种解法:
一种是使用split进行拆分(注意java中的转义写法)
另一种是使用数字权重比较最终值(降低是空间复杂度)
*/
// split函数
String[] v1 = version1.split("\\.");
String[] v2 = version2.split("\\.");
int len1 = v1.length;
int len2 = v2.length;
for(int i = 0; i < len1 || i < len2; i++){
// 没有版本号,则默认为0
int x = 0, y = 0;
if(i < len1){
// 字符串转换为整数
x = Integer.parseInt(v1[i]);
}else{
x = 0;
}
if(i < len2){
// 字符串转换为整数
y = Integer.parseInt(v2[i]);
}else{
y = 0;
}
// 比较
if( x != y){
return x > y ? 1 : -1;
}
}
return 0;
}
}
方法二:采用数字权重的办法
这里要注意内层和外层循环各自的作用,不能简单的只用一层循环(这样位数越长数字就越大),比如 7.5.2.3和7.5.3这样的版本号。
实际上,每一层都得去比较,只不过这里的比较换成了有权重的比较
class Solution {
public int compareVersion(String version1, String version2) {
/**
分析:
所谓的比较版本号,实际上就是在比较大小。这里数字越靠前的,权重越大,那么完全可以将其权重化。
有两种解法:
一种是使用split进行拆分(注意java中的转义写法)
另一种是使用数字权重比较最终值(降低是空间复杂度)
*/
int i = 0,j = 0;
int len1 = version1.length();
int len2 = version2.length();
// 外层循环 控制每一个分位的比较
while(i < len1 || j < len2){
int x = 0, y = 0;
// 内层循环进行权重的叠加
while(i < len1&&version1.charAt(i) != '.'){
// 这里使用前部*10来增加前部的权重
x = x*10 + version1.charAt(i) - '0';
i++;
}
// 跳过.号
++i;
while(j < len2&&version2.charAt(j) != '.'){
y = y*10 + version2.charAt(j) - '0';
j++;
}
// 跳过.号
++j;
if(x != y){
return x > y ? 1: -1;
}
}
return 0;
}
}
345. 反转字符串中的元音字母(双指针+api转换)
class Solution {
public String reverseVowels(String s) {
/**
分析:
题目的意思是找出字符串中所有的元音字母,然后在对应位置的下标进行反转。
第一想法是使用双指针法,在进行逻辑编写的时候还需要注意,将String转换为char数组,char数组转换为string
*/
int len = s.length();
int p1 = 0, p2 = len - 1;
// StringBuilder sb = new StringBuilder(); 使用sb的缺点就是交换的时候不方便
// 思路转换,将String 转换为 char数组,这样就可以原地修改了
// string和char数组互相转换的相关api:s.toCharArray() 和String.valueOf(char[])
char[] res = s.toCharArray();
while( p1 < p2){
while(p1 < p2 && !isVowel(res[p1])){
// 首部循环判断是不是元音
p1++;
}
while(p1 < p2 && !isVowel(res[p2])){
// 尾部循环判断是不是元音
p2--;
}
// 进行交换
char temp = res[p1];
res[p1] = res[p2];
res[p2] = temp;
// 记得移动位置
p1++;
p2--;
}
return String.valueOf(res);
}
public boolean isVowel(char c){
if(c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u'|| c == 'A'|| c == 'E' ||
c == 'I' || c == 'O' || c == 'U'){
return true;
}
return false;
}
}
459. 重复的子字符串(模拟法,%思想)
class Solution {
public boolean repeatedSubstringPattern(String s) {
/**
分析:
题解中使用了indexof()的api函数,个人觉得有点炫技了,所以还是采用可常规思路来解题。
首次就是重复子串构成的字符串必定是某个数的倍数,其次就是确定上一个条件后,依次比较对应的字符是否相同(这里使用了 % 方法,和之前做的环形赛道很像!)
*/
int len = s.length();
for( int i = 1; i < len; i++){
// 长度是i的倍数,说明有可能有戏
if(len % i == 0){
// 循环判断
String sub = s.substring(0,i);
if(judge(sub,s)){
// 是满足条件的直接return(实际上可能有多个满足条件)
return true;
}
}
}
return false;
}
public boolean judge(String sub,String s){
int sublen = sub.length();
for(int i = 0; i < s.length(); i++){
if(s.charAt(i) != sub.charAt(i % sublen)){
// 主串和子串是否匹配,这里使用了 %
return false;
}
}
return true;
}
}
541. 反转字符串 II(模拟法。读懂题意是关键)
class Solution {
public String reverseStr(String s, int k) {
/**
分析:
定义一个反转函数,然后根据题干情况进行依次反转.
这里的题干情况归纳总结就是:反转下标为2k的倍数,反转长度为k,若k大于子串长度,则反转整个子串
*/
int len = s.length();
char[] res = s.toCharArray();
for(int i = 0; i < len; i += 2 * k){
// 这里调用了api,确实比较难想到,还是得多练习
reverse(res,i,Math.min( i + k, len) - 1);
}
return new String(res);
}
public void reverse(char[] res,int start,int end){
// 转换为char数组才能原地修改
while(start < end){
char temp = res[start];
res[start] = res[end];
res[end] = temp;
// 移动
start++;
end--;
}
}
}
557. 反转字符串中的单词 III(双指针)
class Solution {
public String reverseWords(String s) {
/**
很明显,双指针的应用
*/
char[] res = s.toCharArray();
int len = res.length;
int p1 = 0, p2 = 0;
while(p2 < len){
while( p2 < len && res[p2] != ' ' ){
p2++;
}
// 反转
reverse(res,p1,p2-1);
// p2跳过空格
++p2;
p1 = p2;
}
return new String(res);
}
public void reverse(char[] res, int start,int end){
while(start < end){
char temp = res[start];
res[start] = res[end];
res[end] = temp;
start++;
end--;
}
}
}
14. 最长公共前缀(纵向搜索)
class Solution {
public String longestCommonPrefix(String[] strs) {
/**
分析:
这是一道字符串的应用,一般的思路是纵向查找,一一比对前k个字母是否相等
*/
if(strs.length == 0){
return "";
}
for(int i = 0; i < strs[0].length(); i++){
// str[0]中的第 i 个字符
char c = strs[0].charAt(i);
// 纵向搜索
for(int j = 1; j < strs.length; j++){
// i 达到了搜索的长度或者发现有字符不匹配
if(i == strs[j].length() || c != strs[j].charAt(i)){
// 截取字符串
return strs[0].substring(0,i);
}
}
}
// 全都匹配
return strs[0];
}
}