字符串

1. 反转字符串

题目:编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

题目分析:可以直接使用现成的函数;但也有简单进行实现。

class Solution {public void reverseString(char[] s) {int l = 0;int r = s.length - 1;//前后元素进行交换while (l < r) {s[l] ^= s[r];  //构造 a ^ b 的结果,并放在 a 中s[r] ^= s[l];  //将 a ^ b 这一结果再 ^ b ,存入b中,此时 b = a, a = a ^ bs[l] ^= s[r];  //a ^ b 的结果再 ^ a ,存入 a 中,此时 b = a, a = b 完成交换l++;r--;}}public void reverseString(char[] s) {int n = s.length;for(int left = 0, right = n - 1; left < right; left++, right--){char temp = s[left];s[left] = s[right];s[right] = temp;}}
}

2. 反转字符串II

题目:给定一个字符串 s 和一个整数 k,你需要对从字符串开头算起的每隔 2k 个字符的前 k 个字符进行反转。如果剩余字符少于 k 个,则将剩余字符全部反转。如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。

题目分析:在遍历过程中,只要让 i += (2 * k),i 每次移动 2 * k 就可以了,然后判断是否需要有反转的区间。需要固定规律一段一段去处理字符串的时候,要想想在在for循环的表达式上做做文章。

//题目的意思其实概括为 每隔2k个反转前k个,尾数不够k个时候全部反转
class Solution {public String reverseStr(String s, int k) {char[] ch = s.toCharArray();for(int i = 0; i < ch.length; i += 2 * k){int start = i;//这里是判断尾数够不够k个来取决end指针的位置int end = Math.min(ch.length - 1, start + k - 1);//用异或运算反转 while(start < end){ch[start] ^= ch[end];ch[end] ^= ch[start];ch[start] ^= ch[end];start++;end--;}}return new String(ch);}
}

3. 替换空格

题目;请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

题目分析:可以new个新对象复制str是空格就替换;但想要把这道题做到极致,就不要只用额外的辅助空间。

步骤:

  • 扩充数组长度为相应的长度大小。
  • 从后向前替换空格。(这样复杂度低,因为如果是从前向后那每次添加元素都要向后移动元素。

其实很多数组填充类的问题,都可以先预先给数组扩容带填充后的大小,然后在从后向前进行操作。

//使用一个新的对象,复制 str,复制的过程对其判断,是空格则替换,否则直接复制,类似于数组复制
public static String replaceSpace(StringBuffer s) {if (s == null) {return null;}//选用 StringBuilder 单线程使用,比较快,选不选都行StringBuilder sb = new StringBuilder();//使用 sb 逐个复制 str ,碰到空格则替换,否则直接复制for (int i = 0; i < s.length(); i++) {//str.charAt(i) 为 char 类型,为了比较需要将其转为和 " " 相同的字符串类型//if (" ".equals(String.valueOf(str.charAt(i)))){if (s.charAt(i) == ' ') {sb.append("%20");} else {sb.append(s.charAt(i));}}return sb.toString();}//方式二:双指针法
public String replaceSpace(String s) {if(s == null || s.length() == 0){return s;}//扩充空间,空格数量2倍StringBuilder str = new StringBuilder();for (int i = 0; i < s.length(); i++) {if(s.charAt(i) == ' '){str.append("  ");}}//若是没有空格直接返回if(str.length() == 0){return s;}//有空格情况 定义两个指针int left = s.length() - 1;//左指针:指向原始字符串最后一个位置s += str.toString();int right = s.length()-1;//右指针:指向扩展字符串的最后一个位置char[] chars = s.toCharArray();while(left>=0){if(chars[left] == ' '){chars[right--] = '0';chars[right--] = '2';chars[right] = '%';}else{chars[right] = chars[left];}left--;right--;}return new String(chars);
}

4. 翻转字符串里的单词

题目:给定一个字符串,逐个翻转字符串中的每个单词。注意:输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。

题目分析:使用split函数分隔单词,然后定义一个新的string字符串,最后再把单词倒序相加。但是要提高难度,就是不要使用辅助空间,空间复杂度为O(1)。

步骤:

  • 移除多余空格
  • 将整个字符串反转
  • 将每个单词反转
//解法二:创建新字符数组填充。时间复杂度O(n)
class Solution {public String reverseWords(String s) {//源字符数组char[] initialArr = s.toCharArray();//新字符数组char[] newArr = new char[initialArr.length+1];//下面循环添加"单词 ",最终末尾的空格不会返回int newArrPos = 0;//i来进行整体对源字符数组从后往前遍历int i = initialArr.length-1;while(i>=0){while(i>=0 && initialArr[i] == ' '){i--;}  //跳过空格//此时i位置是边界或!=空格,先记录当前索引,之后的while用来确定单词的首字母的位置int right = i;while(i>=0 && initialArr[i] != ' '){i--;} //指定区间单词取出(由于i为首字母的前一位,所以这里+1,),取出的每组末尾都带有一个空格for (int j = i+1; j <= right; j++) {newArr[newArrPos++] = initialArr[j];if(j == right){newArr[newArrPos++] = ' ';//空格}}}//若是原始字符串没有单词,直接返回空字符串;若是有单词,返回0-末尾空格索引前范围的字符数组(转成String返回)if(newArrPos == 0){return "";}else{return new String(newArr,0,newArrPos-1);}}
}

5. 左旋转字符串

题目:字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。

题目分析:可以使用整体反转+局部反转实现。

步骤:

  • 反转区间为前n的子串
  • 反转区间为n到末尾的子串
  • 反转整个字符串

class Solution {public String reverseLeftWords(String s, int n) {int len = s.length();StringBuilder sb = new StringBuilder(s);reverseString(sb, 0, n - 1);reverseString(sb, n, len - 1);return sb.reverse().toString();}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--;}}
}

6. 实现strStr()

题目:给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。对于本题而言,当 needle 是空字符串时我们应当返回 0

KMP算法

KMP的经典思想就是:当出现字符串不匹配时,可以记录一部分之前已经匹配的文本内容,利用这些信息避免从头再去做匹配。

next数组就是一个前缀表。**前缀表是用来回退的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配。**什么是前缀表:记录下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀。

前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串

得出前缀表无论是统一减一还是不减一得到的next数组仅仅是kmp的实现方式的不同。以下就是直接使用next数组来做匹配。

构造next数组其实是就是计算模式串s,前缀表的过程。步骤:

  • 1.初始化
  • 2.处理前后缀不相同的情况
  • 3.处理前后缀相同的情况
  • 4.更新next数组
  void getNext(int[] next, string s) {int j = 0;next[0] = 0;for(int i = 1; i < s.size(); i++) {while (j > 0 && s[i] != s[j]) { // j要保证大于0,因为下面有取j-1作为数组下标的操作j = next[j - 1]; // 注意这里,是要找前一位的对应的回退位置了}if (s[i] == s[j]) {j++;}next[i] = j;}}

给出滑动窗口和KMP算法解决的两种代码

// 方法一
class Solution {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 int strStr(String haystack, String needle) {if(needle.length()==0){return 0;}int[] next = new int[needle.length()];getNext(next, needle);int j = -1;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++;}if(j==needle.length()-1){return (i-needle.length()+1);}}return -1;}
}class Solution {//前缀表(不减一)Java实现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++) {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 - needle.length() + 1;}return -1;}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];if (s.charAt(j) == s.charAt(i)) j++;next[i] = j; }}
}
class Solution {/*** 基于窗口滑动的算法* <p>* 时间复杂度:O(m*n)* 空间复杂度:O(1)* 注:n为haystack的长度,m为needle的长度*/public int strStr(String haystack, String needle) {int m = needle.length();// 当 needle 是空字符串时我们应当返回 0if (m == 0) {return 0;}int n = haystack.length();if (n < m) {return -1;}int i = 0;int j = 0;while (i < n - m + 1) {// 找到首字母相等while (i < n && haystack.charAt(i) != needle.charAt(j)) {i++;}if (i == n) {// 没有首字母相等的return -1;}// 遍历后续字符,判断是否相等i++;j++;while (i < n && j < m && haystack.charAt(i) == needle.charAt(j)) {i++;j++;}if (j == m) {// 找到return i - j;} else {// 未找到i -= j - 1;j = 0;}}return -1;}
}

7. 重复的子字符串

题目:给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。

题目分析:标准的KMP算法题目。

如果 next[len - 1] != -1,则说明字符串有最长相同的前后缀(就是字符串里的前缀子串和后缀子串相同的最长长度)。最长相等前后缀的长度为:next[len - 1] + 1。(这里的next数组是以统一减一的方式计算的,因此需要+1)数组长度为:len。如果len % (len - (next[len - 1] + 1)) == 0 ,则说明 (数组长度-最长相等前后缀的长度) 正好可以被 数组的长度整除,说明有该字符串有重复的子字符串。数组长度减去最长相同前后缀的长度相当于是第一个周期的长度,也就是一个周期的长度,如果这个周期可以被整除,就说明整个数组就是这个周期的循环。

class Solution {public boolean repeatedSubstringPattern(String s) {if (s.equals("")) return false;int len = s.length();// 原串加个空格(哨兵),使下标从1开始,这样j从0开始,也不用初始化了s = " " + s;char[] chars = s.toCharArray();int[] next = new int[len + 1];// 构造 next 数组过程,j从0开始(空格),i从2开始for (int i = 2, j = 0; i <= len; i++) {// 匹配不成功,j回到前一位置 next 数组所对应的值while (j > 0 && chars[i] != chars[j + 1]) j = next[j];// 匹配成功,j往后移if (chars[i] == chars[j + 1]) j++;// 更新 next 数组的值next[i] = j;}// 最后判断是否是重复的子字符串,这里 next[len] 即代表next数组末尾的值if (next[len] > 0 && len % (len - next[len]) == 0) {return true;}return false;}
}

相关算法---字符串相关推荐

  1. Algorithm:C++语言实现之字符串相关算法(字符串的循环左移、字符串的全排列、带有同个字符的全排列、串匹配问题的BF算法和KMP算法)

    Algorithm:C++语言实现之字符串相关算法(字符串的循环左移.字符串的全排列.带有同个字符的全排列.串匹配问题的BF算法和KMP算法) 目录 一.字符串的算法 1.字符串的循环左移 2.字符串 ...

  2. Algorithm:树相关算法(BBT/BST/B树/R树)简介(二叉查找树、二叉查找树的插入节点、二叉查找树的删除、二叉树的遍历、平衡二叉树)C 语言实现

    Algorithm:树相关算法(BBT/BST/B树/R树)简介(二叉查找树.二叉查找树的插入节点.二叉查找树的删除.二叉树的遍历.平衡二叉树)C++语言实现 目录 树的基础知识 1.二叉树的遍-前序 ...

  3. 15-垃圾回收相关算法

    垃圾回收相关算法 标记阶段:引用计数算法(Java未使用) 垃圾标记阶段:对象存活判断 在堆里存放着几乎所有的Java对象实例,在GC执行垃圾回收之前,首先需要区分出内存中哪些是存活对象,哪些是已经死 ...

  4. JVM学习笔记之-拉圾回收概述,垃圾回收相关算法

    拉圾回收概述 什么是垃圾 垃圾收集,不是Java语言的伴生产物.早在1960年,第一门开始使用内存动态分配和垃圾收集技术的Lisp语言诞生. 关于垃圾收集有三个经典问题: 哪些内存需要回收? 什么时候 ...

  5. 聚类分析与相关算法(Kmeans等)详解

    聚类分析与相关算法详解 1 距离度量 1.1欧几里得距离定义 1.2其他距离度量的补充 1.3余弦距离 1.4简单匹配系数 1.5jaccard系数 1.6海明距离 2 聚类问题 2.1非监督学习中的 ...

  6. 第 15 章 垃圾回收相关算法

    第 15 章 垃圾回收相关算法 1.标记阶段:引用计数器 1.1.标记阶段的目的 垃圾标记阶段:判断对象是否存活 在堆里存放着几乎所有的Java对象实例,在GC执行垃圾回收之前,首先需要区分出内存中哪 ...

  7. 第十五章 - 垃圾回收相关算法

    第十五章 - 垃圾回收相关算法 文章目录 第十五章 - 垃圾回收相关算法 1.标记阶段:引用计数算法 1.1 垃圾标记阶段:对象存活判断 1.2 引用计数算法 1.3 小结 2.标记阶段:可达性分析算 ...

  8. 第七篇章——垃圾回收概念及相关算法

    垃圾回收--概述 本专栏学习内容来自尚硅谷宋红康老师的视频以及<深入理解JVM虚拟机>第三版 有兴趣的小伙伴可以点击视频地址观看,也可以点击下载电子书 垃圾回收概述 垃圾回收不是Java语 ...

  9. 【Java】数组Array(概念、相关语法、相关算法)

    数组 文章目录 数组 1.数组的相关概念和名词(了解) 2 .数组的相关语法 2.1. 数组的声明 2.2. 数组的初始化 2.3.数组的遍历 2.4. 数组的内存分析 3. 数组的相关算法 3.1. ...

最新文章

  1. python语言入门n-Python基础语法学习笔记
  2. 微信小程序swiper禁止用户手动滑动
  3. JavaScript 面向对象
  4. 上线随想之2011-03-26
  5. oracle 创建用户且只有查看权限
  6. template 不能分别在.h和.cpp中定义模板
  7. recvmsg和sendmsg函数
  8. 人口各省预测模型matlab_MATLAB实验9河南省人口预测模型马尔萨斯人口模型.doc
  9. XSS自动化工具——Beef
  10. 重新回归最初始的51单片机,跟我一起学单片机吧(内含单片机驱动、烧录软件、程序开发软件的安装和使用)
  11. Fang Fang 思维题
  12. 期货反向跟单—日内交易是否合适
  13. 证券机构分析师研报靠谱么?关于波司登沽空与买入报告
  14. Sec-scan漏洞扫描系统
  15. 使用CenterNet训练自己的数据集
  16. 报错:module ‘brotli‘ has no attribute ‘error‘
  17. app:processDebugManifest 错误
  18. SQL执行顺序与书写顺序
  19. BZOJ3238[Ahoi2013]差异
  20. 数通基础-TCPIP参考模型

热门文章

  1. HTTP、HTTPS
  2. TCP 与 UDP 的区别
  3. [react] This synthetic event is reused for performance reasons. If you‘re seeing this, you‘re access
  4. 应用程序未安装:已安装了存在签名冲突的同名数据包。
  5. 03 分布式系统的技术栈
  6. ACM--博弈论入门--几种博弈游戏类型+题集
  7. MATLAB simulink 模型验证学习笔记
  8. 什么是CISP-PTE证书?考什么?
  9. Python攻关之模块(1)
  10. 【机试】2011-2020年复旦大学考研复试机试真题