算法-字符串
1. 反转字符串(LeetCode 344)
题目:编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
思路:双指针,左边和右边对应位置的依次交换
class Solution {
public void reverseString(char[] s) {
//两个指针从两端到中间,依次交换
int left = 0;
int right = s.length - 1;
char temp;
while(left < right){
temp = s[left];
s[left] = s[right];
s[right] = temp;
left++;
right--;
}
}
}
2. 反转字符串II(LeetCode 541)
题目:给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。
- 如果剩余字符少于 k 个,则将剩余字符全部反转。
- 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
java:
- string->char[]:
char[] ch = str.toCharArray();
- char[]->string:
str = new String(ch);
class Solution {
public String reverseStr(String s, int k) {
char[] ch = s.toCharArray(); //string -> char[]
int n = ch.length / (2*k);
int last = ch.length - n*2*k;
//处理结尾不足2k的一段
if(last < k) reverse(ch, n*2*k, ch.length-1);
else reverse(ch, n*2*k, n*2*k + k - 1);
//处理前面
for(int i = 0; i<n; ++i){
reverse(ch, i*2*k, i*2*k + k - 1);
}
return new String(ch); //char[] -> string
}
public void reverse(char[] ch, int i, int j){
char temp;
while(i < j){
temp = ch[i];
ch[i] = ch[j];
ch[j] = temp;
i++;
j--;
}
}
}
3. 反转字符串中的单词(LeetCode 151)
题目:给你一个字符串 s ,请你反转字符串中 单词 的顺序。
单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。
返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。
注意:
- 输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。
- 返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。
示例:
输入:s = " hello world "
输出:"world hello"
解释:反转后的字符串中不能存在前导空格和尾随空格;
如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。
class Solution {
//用 char[] 来实现 String 的 removeExtraSpaces,reverse 操作
public String reverseWords(String s) {
char[] chars = s.toCharArray();
chars = removeExtraSpaces(chars); //去除多余空格
reverse(chars, 0, chars.length - 1); //反转整个串
reverseEachWord(chars); //反转每个单词
return new String(chars);
}
//1. 去除首尾以及中间的多余空格
public char[] removeExtraSpaces(char[] chars){
int fast = 0;
int slow = 0;
for(fast = 0; fast<chars.length; ++fast){
if(chars[fast] != ' '){
//除第一个单词外,单词末尾要加空格
if(slow!=0){
chars[slow] = ' ';
slow++;
}
//逐个单词处理
while(fast < chars.length && chars[fast]!=' '){
chars[slow++] = chars[fast++];
}
}
}
char[] newchars = new char[slow];
System.arraycopy(chars, 0, newchars, 0, slow);
return newchars;
}
//2.双指针实现指定范围内字符串反转,可参考字符串反转题解
public void reverse(char[] chars, int left, int right) {
if (right >= chars.length) {
System.out.println("set a wrong right");
return;
}
while (left < right) {
chars[left] ^= chars[right];
chars[right] ^= chars[left];
chars[left] ^= chars[right];
left++;
right--;
}
}
//3.单词反转
public void reverseEachWord(char[] chars) {
int start = 0;
//end <= s.length() 这里的 = ,是为了让 end 永远指向单词末尾后一个位置,这样 reverse 的实参更好设置
for (int end = 0; end <= chars.length; end++) {
// end 每次到单词末尾后的空格或串尾,开始反转单词
if (end == chars.length || chars[end] == ' ') {
reverse(chars, start, end - 1);
start = end + 1;
}
}
}
}
4. 右旋转字符串(卡码网)
题目:字符串的右旋转操作是把字符串尾部的若干个字符转移到字符串的前面。给定一个字符串 s 和一个正整数 k,请编写一个函数,将字符串中的后面 k 个字符移到字符串的前面,实现字符串的右旋转操作。
例如,对于输入字符串 "abcdefg" 和整数 2,函数应该将其转换为 "fgabcde"。
模式:
- LeetCode采用的是核心代码模式,只需要写算法部分,输入输出的接口都已经定义好了
- 本题采用的是ACM模式,需要自己处理输入和输出
思路:反转3次,整体反转1次,2个部分各自反转1次
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner in = new Scanner(System.in);
int n = Integer.parseInt(in.nextLine());
String s = in.nextLine();
char[] chars = s.toCharArray();
n = n % (chars.length);
reverse(chars, 0, chars.length-1);
reverse(chars, 0, n-1);
reverse(chars, n, chars.length-1);
System.out.println(chars);
}
public static void reverse(char[] chars, int start, int end){
int left = start;
int right = end;
char temp;
while(left < right){
temp = chars[left];
chars[left] = chars[right];
chars[right] = temp;
left++;
right--;
}
}
}
5. 使用KMP算法 实现 strStr() (LeetCode 28)
题目:给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1 。
class Solution {
//KMP算法
public int strStr(String haystack, String needle) {
int[] next = new int[needle.length()];
getNext(next, needle);
int j = 0; //指向needle
for(int i = 0; i<haystack.length(); ++i){
//要么找到最长相等前后缀,要么j回退到0
while (j > 0 && needle.charAt(j) != haystack.charAt(i))
j = next[j - 1];
if (needle.charAt(j) == haystack.charAt(i))
j++;
if(j == needle.length())
return i - j + 1;
}
return -1;
}
//next为前缀表
public void getNext(int[] next, String str){
int j = 0; //前缀的最后
int i = 1; //后缀的最后
next[0] = 0;
for(i = 1; i<str.length(); ++i){
//回退到next[j-1]的位置上
while(j > 0 && str.charAt(i) != str.charAt(j))
j = next[j-1];
if(str.charAt(i) == str.charAt(j))
j++;
next[i] = j;
}
}
}
6. 重复的子字符串(LeetCode 459)
题目:给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。
//利用KMP解题
//如果由字串重复多次构成,则最长相等前后缀不包含的部分即为重复单位
class Solution {
public boolean repeatedSubstringPattern(String s) {
int[] next = new int[s.length()];
getNext(next, s);
int maxLengthPrefix = next[s.length()-1];
if(maxLengthPrefix > 0 && s.length() % (s.length()-maxLengthPrefix) == 0)
return true;
return false;
}
public void getNext(int[] next, String str){
int i = 1; //后缀的最后一个
int j = 0; //前缀的最后一个
next[0] = 0;
for(i = 1; i<str.length(); ++i){
while(j>0 && str.charAt(i) != str.charAt(j))
j = next[j-1];
if(str.charAt(i) == str.charAt(j))
j++;
next[i] = j;
}
}
}