算法题总结(八)——字符串
531、反转字符串二
给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。
- 如果剩余字符少于 k 个,则将剩余字符全部反转。
- 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
示例 1:
输入:s = "abcdefg", k = 2
输出:"bacdfeg"
示例 2:
输入:s = "abcd", k = 2
输出:"bacd"
要规律的处理字符串的时候,在for循环上,每次有规律的递增,从开始+k,判断与总长度的大小。
class Solution {public String reverseStr(String s, int k) {char[] ch=s.toCharArray();for(int i=0;i<ch.length;i+=2*k) //控制每次循环的开头{//只要剩余的字符大于等于k个,就翻转从i开始的k个字符。包括两种情况:剩余长度大于等于2k、在k和2k中间。if(i+k-1<ch.length) //从i开始的k个字符,序号是i+k-1 {reverse(ch,i,i+k-1);}//剩余字符少于 k 个,则将剩余字符全部反转else{reverse(ch,i,ch.length-1);}}return new String(ch);}//定义一个反转函数public void reverse(char[] ch,int i,int j){while(i<j){char t=ch[i];ch[i]=ch[j];ch[j]=t;i++;j--;}}
}
ACM模式-替换数字
给定一个字符串 s,它包含小写字母和数字字符,请编写一个函数,将字符串中的字母字符保持不变,而将每个数字字符替换为number。
例如,对于输入字符串 “a1b2c3”,函数应该将其转换为 “anumberbnumbercnumber”。
对于输入字符串 “a5b”,函数应该将其转换为 “anumberb”
输入:一个字符串 s,s 仅包含小写字母和数字字符。
输出:打印一个新的字符串,其中每个数字字符都被替换为了number
样例输入:a1b2c3
样例输出:anumberbnumbercnumber
数据范围:1 <= s.length < 10000。
使用一个StringBuilder
import java.util.*; class Main {public static void main(String[] args) {Scanner in = new Scanner(System.in);String s = in.nextLine();StringBuilder sb = new StringBuilder();for (int i = 0; i < s.length(); i++) {if (Character.isDigit(s.charAt(i))) {sb.append("number");}else sb.append(s.charAt(i));}System.out.println(sb);}
}
151、反转字符串中的单词
给你一个字符串 s ,请你反转字符串中 单词 的顺序。
单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。
返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。
注意:输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。
示例 1:
输入:s = "the sky is blue"
输出:"blue is sky the"
示例 2:
输入:s = " hello world "
输出:"world hello"
解释:反转后的字符串中不能存在前导空格和尾随空格。
示例 3:
输入:s = "a good example"
输出:"example good a"
解释:如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。
思路:整体反转+局部反转
class Solution {/*** 不使用Java内置方法实现* <p>* 1.去除首尾以及中间多余空格* 2.反转整个字符串* 3.反转各个单词*/public String reverseWords(String s) {// System.out.println("ReverseWords.reverseWords2() called with: s = [" + s + "]");// 1.去除首尾以及中间多余空格StringBuilder sb = removeSpace(s);// 2.反转整个字符串reverseString(sb, 0, sb.length() - 1);// 3.反转各个单词reverseEachWord(sb);return sb.toString();}private StringBuilder removeSpace(String s) {// System.out.println("ReverseWords.removeSpace() called with: s = [" + s + "]");int start = 0;int end = s.length() - 1;while (s.charAt(start) == ' ') start++;while (s.charAt(end) == ' ') end--;StringBuilder sb = new StringBuilder();//去除中间的空格while (start <= end) {char c = s.charAt(start);//如果不是空格,或者是第一个空格,就加入sb中if (c != ' ' || sb.charAt(sb.length() - 1) != ' ') {sb.append(c);}start++;}return sb;}/*** 反转字符串指定区间[start, end]的字符*/public void reverseString(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--;}}private void reverseEachWord(StringBuilder sb) {int start = 0;int end = 0;int n = sb.length();//使用双指针,对每个单词进行翻转while (end < n) {while (sb.charAt(end) != ' ') {end++;}reverseString(sb, start, end - 1);start = end + 1;end = start;}}
}
这道题的关键点就是1、使用sb去除中间的字符串,2、先整体翻转,再使用双指针逐个单词进行翻转。
ACM模式-右旋字符串
字符串的右旋转操作是把字符串尾部的若干个字符转移到字符串的前面。给定一个字符串 s 和一个正整数 k,请编写一个函数,将字符串中的后面 k 个字符移到字符串的前面,实现字符串的右旋转操作。
例如,对于输入字符串 “abcdefg” 和整数 2,函数应该将其转换为 “fgabcde”。
输入:输入共包含两行,第一行为一个正整数 k,代表右旋转的位数。第二行为字符串 s,代表需要旋转的字符串。
输出:输出共一行,为进行了右旋转操作后的字符串。
样例输入:
2
abcdefg
样例输出:
fgabcde
数据范围:1 <= k < 10000, 1 <= s.length < 10000;
import java.util.Scanner; //导入包public class Main {public static void main(String[] args) {Scanner in = new Scanner(System.in);int n = in.nextInt();String s = in.nextLine();int len = s.length(); //获取字符串长度char[] chars = s.toCharArray();reverseString(chars, 0, len - 1); //反转整个字符串reverseString(chars, 0, n - 1); //反转前一段字符串,此时的字符串首尾尾是0,n - 1reverseString(chars, n, len - 1); //反转后一段字符串,此时的字符串首尾尾是n,len - 1System.out.println(chars);}public static void reverseString(char[] ch, int start, int end) {while (start < end) {char t=ch[start];ch[start]=ch[end];ch[end]=t;start++;end--;}}
}
28、找出字符串中第一个匹配项的下标
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1 。
示例 1:
输入:haystack = "sadbutsad", needle = "sad"
输出:0
解释:"sad" 在下标 0 和 6 处匹配。
第一个匹配项的下标是 0 ,所以返回 0 。
示例 2:
输入:haystack = "leetcode", needle = "leeto"
输出:-1
解释:"leeto" 没有在 "leetcode" 中出现,所以返回 -1 。
即KMP算法:
KMP的经典思想就是:当出现字符串不匹配时,可以记录一部分之前已经匹配的文本内容,利用这些信息避免从头再去做匹配。
前缀表是用来回退的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配。
那么什么是前缀表:记录下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀
class Solution {//给字符串s构造next数组private void getNext(int[] next ,String s){int j=0;next[0]=0;for(int i=1;i<s.length();i++){//不相等的话,一直回溯到相等while(j>0 && s.charAt(j)!=s.charAt(i)){j=next[j-1];}//相等的话;j++if(s.charAt(i)==s.charAt(j))j++;next[i]=j;}}public int strStr(String haystack, String needle) {if(needle.length()==0)return 0;int[] next=new int[needle.length()];getNext(next,needle);int j=0;for(int i=0;i<haystack.length();i++){//不相等的时候,用next数组回溯while(j>0 && haystack.charAt(i)!=needle.charAt(j))j=next[j-1];if(haystack.charAt(i)==needle.charAt(j))j++;if(j==needle.length())return i+1-j;}return -1;}
}
459、重复的子字符串
给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。
示例 1:
输入: s = "abab"
输出: true
解释: 可由子串 "ab" 重复两次构成。
示例 2:
输入: s = "aba"
输出: false
示例 3:
输入: s = "abcabcabcabc"
输出: true
解释: 可由子串 "abc" 重复四次构成。 (或子串 "abcabc" 重复两次构成。)
移动匹配
当一个字符串s:abcabc,内部由重复的子串组成,那么这个字符串的结构一定是这样的:
也就是由前后相同的子串组成。
那么既然前面有相同的子串,后面有相同的子串,用 s + s,这样组成的字符串中,后面的子串做前串,前面的子串做后串,就一定还能组成一个s,如图:
所以判断字符串s是否由重复子串组成,只要两个s拼接在一起,里面还出现一个s的话,就说明是由重复子串组成。
当然,我们在判断 s + s 拼接的字符串里是否出现一个s的的时候,要刨除 s + s 的首字符和尾字符,这样避免在s+s中搜索出原来的s,我们要搜索的是中间拼接出来的s。
class Solution {public boolean repeatedSubstringPattern(String s) {//由于是重复构成的,所以两个拼接,中间一定能找到子串sint n=s.length();String str=s+s;String substr =str.substring(1,2*n-1); //去头去尾int index=substr.indexOf(s); if(index != -1) return true;else return false;}
}
字符串常用方法:
双指针法、多次反转,转化为sb