public void reverseString(char[] s) {
int len = s.length;
for(int i = 0;i < len / 2;i ++){
char c = s[i];
s[i] = s[len - i - 1];
s[len - i - 1] = c;
}
}
public void reverseString(char[] s) {
int len = s.length;
int left = 0,right = len - 1;
while(left < right){
char temp = s[left];
s[left] = s[right];
s[right] = temp;
left ++;
right --;
}
}
//3.位运算
public void reverseString3(char[] s) {
int l = 0,r = s.length - 1;
while(l < r){
s[l] ^= s[r];
s[r] ^= s[l];
s[l] ^= s[r];
l ++;
r --;
}
}
public String reverseStr(String s, int k) {
int len = s.length();
char[] c = s.toCharArray();
for(int i = 0;i < len;i += 2 * k){
reverse(c,i,Math.min(i + k,len) - 1);
}
return new String(c);
}
//反转传入的字符数组
public void reverse(char[] c,int left,int right){
while(left < right){
char temp = c[left];
c[left] = c[right];
c[right] = temp;
left ++;
right --;
}
}
//1.使用String类提供的方法
public String replaceSpace(String s) {
return s.replaceAll(" ","%20");
}
//2.使用stringbuilder
public String replaceSpace(String s) {
StringBuilder sb = new StringBuilder();
for(int i = 0;i < s.length();i ++){
char c = s.charAt(i);
if(c == ' '){
sb.append("%20");
continue;
}
sb.append(c);
}
return sb.toString();
}
//3.使用string
//注意:和第二个方法相比较,这个方法的性能下降许多,后来查了一下,
// 在《Think in Java》这本书中看到了一章关于字符串的性能说明,
//1.String对象每执行一次“+”操作都会产生一个新的String对象,
//2.StringBuilder执行连接操作只有一个对象
public String replaceSpace(String s) {
String res = "";
for(int i = 0;i < s.length();i ++){
char c = s.charAt(i);
if(c == ' '){
res += "%20";
continue;
}
res += c;
}
return res;
}
//1.不使用java库函数,自己实现
public static String reverseWords(String s) {
//去掉字符串中多余的空格
StringBuilder sb = trimSpaces(s);
// 翻转字符串
reverse(sb,0,sb.length() - 1);
// 翻转每个单词
reverseEachWords(sb);
return sb.toString();
}
public static StringBuilder trimSpaces(String s){
int left = 0,right = s.length() - 1;
// 去掉字符串开头的空白字符
while(left <= right && s.charAt(left) == ' '){
left ++;
}
// 去掉字符串末尾的空白字符
while(left <= right && s.charAt(right) == ' '){
right --;
}
// 将字符串间多余的空白字符去除
StringBuilder sb = new StringBuilder();
while(left <= right){
char c = s.charAt(left);
if(c != ' '){
sb.append(c);
}else if(sb.charAt(sb.length() - 1) != ' '){
sb.append(c);
}
left ++;
}
return sb;
}
public static void reverse(StringBuilder sb, int left, int right) {
while (left < right) {
char tmp = sb.charAt(left);
sb.setCharAt(left++, sb.charAt(right));
sb.setCharAt(right--, tmp);
}
}
public static void reverseEachWords(StringBuilder sb){
int len = sb.length();
int start = 0,end = 0;
while(start < len){
while(end < len && sb.charAt(end) != ' '){
end ++;
}
reverse(sb,start,end - 1);
start = end + 1;
end ++;
}
}
public String reverseWords(String s) {
int len = s.length();
int left = 0,right = len - 1;
// 去掉字符串开头的空白字符
while(left <= right && s.charAt(left) == ' '){
left ++;
}
// 去掉字符串末尾的空白字符
while(left <= right && s.charAt(right) == ' '){
right --;
}
Deque<String> deque = new ArrayDeque<String>();
StringBuilder sb = new StringBuilder();
//从前往后遍历,将单词添加到队列的前端
while(left <= right){
char c = s.charAt(left);
if(sb.length() != 0 && c == ' '){
deque.offerFirst(sb.toString());
//清空StringBuilder
sb.setLength(0);
}else if(c != ' '){
sb.append(c);
}
left ++;
}
//不要忘了这一步,当遍历完最后一个单词,直接退出while循环,此时队列中还没有加入最后一个单词
deque.offerFirst(sb.toString());
return String.join(" ",deque);
}
// 1.字符串切片
public String reverseLeftWords(String s, int n) {
return s.substring(n,s.length()) + s.substring(0,n);
}
//2.如果面试要求不能使用库函数,利用StringBuilder
//每轮遍历拼接字符时,只是向列表尾部添加一个新的字符元素。
//最终拼接转化为字符串时,系统 仅申请一次内存 。
public String reverseLeftWords(String s, int n) {
int len = s.length();
StringBuilder sb = new StringBuilder();
//先将后面的放入StringBuilder中
for(int i = n;i < len;i ++){
sb.append(s.charAt(i));
}
//再将前面的放入StringBuilder中
for(int i = 0;i < n;i ++){
sb.append(s.charAt(i));
}
return sb.toString();
}
//3.如果只能使用字符串,构造一个空字符串,依次拼接
//每轮遍历拼接字符时,都需要新建一个字符串;
//系统 需申请 N 次内存 ,数据量较大时效率低下。
String res = "";
for(int i = n;i < s.length();i ++){
res += s.charAt(i);
}
for(int i = 0;i < n;i ++){
res += s.charAt(i);
}
return res;
利用取余操作,简化代码
public String reverseLeftWords(String s, int n) {
StringBuilder sb = new StringBuilder();
for(int i = n;i < n + s.length();i ++){
//取余操作,简化代码
sb.append(s.charAt(i % s.length()));
}
return sb.toString();
}
其实还有一种方法,在4、翻转字符串里的单词中,是先整体反转,再局部反转
本题中,可以先局部反转,再整体反转
例如: abcdefg k=2
局部反转:bagfedc
整体反转:cdefgab
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 StringBuilder 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 --;
}
return sb;
}
//1.利用substring
public int strStr(String haystack, String needle) {
if(needle == "") return 0;
int len1 = haystack.length(),len2 = needle.length();
for(int i = 0;i < len1 - len2 + 1;i ++){
if(haystack.substring(i,i + len2).equals(needle)){
return i;
}
}
return -1;
}
//2.暴力匹配
public int strStr2(String haystack, String needle) {
char[] c1 = haystack.toCharArray();
char[] c2 = needle.toCharArray();
int s1Length = haystack.length();
int s2Length = needle.length();
int i = 0,j = 0;
while (i < s1Length && j < s2Length){
if (c1[i] == c2[j]){
i ++;
j ++;
if (j == s2Length){
//返回第一次出现的位置
return i - j;
}
}else{
//回到上一次开始比较的下一个位置
i = i - j + 1;
j = 0;
}
}
return -1;
}
//3.暴力匹配
public int strStr3(String haystack, String needle) {
char[] c1 = haystack.toCharArray();
char[] c2 = needle.toCharArray();
int m = c1.length;
int n = c2.length;
for(int i = 0;i <= m - n;i ++){
int index1 = i,index2 = 0;
while(index2 < n && c1[index1] == c2[index2]){
index1 ++;
index2 ++;
}
if(index2 == n) return index1 - index2;
}
return -1;
}
当出现字符串不匹配时,可以记录一部分之前已经匹配的文本内容,利用这些信息避免从头再去做匹配。看这里
n为文本串长度,m为模式串长度,因为在匹配的过程中,根据前缀表不断调整匹配的位置,可以看出匹配的过程是 O ( n ) O(n) O(n),之前还要单独生成next数组,时间复杂度是 O ( m ) O(m) O(m)。所以整个KMP算法的时间复杂度是 O ( n + m ) O(n+m) O(n+m)的。
//4.KMP算法
public int strStr4(String s1,String s2){
if(s2 == " ") return 0;
int[] next = kmpNext(s2);
for (int i = 0,j = 0; i < s1.length(); i++) {
/*
* s1:文本串 s2:模式串
* a a b a a b a a f
* i
* 0 1 2 3 4 5
* a a b a a f
* j
*
*此时b != f , j 回退到 j == 2,因为知道文本串中有aa和模式串中aa相等,
*而模式串自己0和1位置的aa和3,4位置的aa相等,所以aa不用再做比较。
*如果j==2时仍然不相等,接着回退,以此类推...
*所以用while
* */
while (j > 0 && s1.charAt(i) != s2.charAt(j)){
j = next[j - 1];
}
if (s1.charAt(i) == s2.charAt(j)) {
j ++;
}
if (j == s2.length()){
return i - j + 1;
}
}
return -1;
}
//获取一个字符串的部分匹配值表
public static int[] kmpNext(String s){
//1.初始化
int[] next = new int[s.length()];
next[0] = 0;
for (int i = 1,j = 0; i < s.length(); i++) {
/*
* a b a b a b a f
*
* 0 0 1 2 3 4 5 0
*
* 前缀:指不包含最后一个字符的所有以第一个字符开头的连续子串
* 后缀:指不包含第一个字符的所有以最后一个字符结尾的连续子串
* */
while (j > 0 && s.charAt(i) != s.charAt(j)){
j = next[j - 1];
}
//3.前后缀相同
if (s.charAt(i) == s.charAt(j)){
j ++;
}
//4.填充next数组
next[i] = j;
}
return next;
}
//1.自己想的
//将s中所有可能的子串全部挑出来,看子串是否能组成s
public boolean repeatedSubstringPattern(String s) {
int len = s.length();
for(int i = 1;i <= len / 2;i ++){
//做一个优化
if(len % i != 0) continue;
StringBuilder sb = new StringBuilder();
while(sb.length() < len){
sb.append(s.substring(0,i));
}
if(sb.toString().equals(s)){
return true;
}
}
return false;
}
//2.枚举
public boolean repeatedSubstringPattern(String s) {
int len = s.length();
//i是可能的子串的长度
for(int i = 1;i <= len / 2;i ++){
if(len % i == 0){
boolean match = true;
for(int j = i;j < len;j ++){
if(s.charAt(j) != s.charAt(j - i)){
match = false;
break;
}
}
if(match){
return true;
}
}
}
return false;
}
//3.使用库函数
public boolean repeatedSubstringPattern3(String s) {
//indexOf(s,index) 从index下标开始找s首次出现的起始下标
//如果s = ab ,s + s = abab,起始下标==s.length(),返回false
//如果s=abab,s + s = abababab,起始下标为2<4,返回true
//其实就是令s1=s,s2=s,令t=s1+s2,看是否能从t中找到s的第一次出现位置
//且此位置不为0或s.length(),即看是否能利用s1的后面组合s2的前面得到s
return (s + s).indexOf(s,1) < s.length();
}
//4.kmp算法,思路和3一样,只不过查找过程换为kmp算法
public boolean repeatedSubstringPattern4(String s) {
String t = s + s;
return Kmp(t,s) == s.length() ? false : true;
}
public int Kmp(String t,String s){
int[] next = getNext(s);
for(int i = 1,j = 0;i < t.length();i ++){
while(j > 0 && t.charAt(i) != s.charAt(j)){
j = next[j - 1];
}
if(t.charAt(i) == s.charAt(j)){
j ++;
}
if(j == s.length()){
return i - j + 1;
}
}
return -1;
}
public int[] getNext(String s){
int[] next = new int[s.length()];
next[0] = 0;
for(int i = 1,j = 0;i < s.length();i ++){
while(j > 0 && s.charAt(i) != s.charAt(j)){
j = next[j - 1];
}
if(s.charAt(i) == s.charAt(j)){
j ++;
}
next[i] = j;
}
return next;
}
//5.kmp算法
//5.kmp算法
public boolean repeatedSubstringPattern5(String s) {
int len = s.length();
int[] next = new int[len];
next[0] = 0;
for(int i = 1,j = 0;i < len;i ++){
while(j > 0 && s.charAt(i) != s.charAt(j)){
j = next[j - 1];
}
if(s.charAt(i) == s.charAt(j)){
j ++;
}
next[i] = j;
}
//构造出next数组之后,以abab为例,最长公共前后缀的长度是2,4 % (4 - 2) == 0,返回true
//假设s串是可以由数个子串构成的,那么len - 最长公共前后缀的长度后,就是可构成s的最短子串
//的长度,最后看len是否是这个最短子串长度的整数倍
if(next[len - 1] > 0 && len % (len - next[len - 1]) == 0){
return true;
}
return false;
}