一、右旋字符串
但是要求不能申请额外空间,只能在本串上操作:
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
in.nextLine();
String s = in.nextLine();
s = rightreverse(s,n);
System.out.println(s);
}
public static String rightreverse(String s, int n) {
return s.substring(s.length()-n)+s.substring(0,s.length()-n);
}
}
注意substring函数左闭右开。
还有一种思路是,先将整个字符串反转,然后分别将前n个字符和后length-n个字符串反转,相当于调用三次反转函数(双指针,可以用位运算,也可以用中间值temp换),具体代码如下:
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();
int len = s.length(); //获取字符串长度
char[] chars = s.toCharArray();
reverseString(chars, 0, len - 1); //反转整个字符串
reverseString(chars, 0, n - 1); //反转前一段字符串,此时的字符串首尾尾是0,n - 1
reverseString(chars, n, len - 1); //反转后一段字符串,此时的字符串首尾尾是n,len - 1
System.out.println(chars);
}
public static void reverseString(char[] ch, int start, int end) {
//异或法反转字符串,参照题目 344.反转字符串的解释
while (start < end) {
ch[start] ^= ch[end];
ch[end] ^= ch[start];
ch[start] ^= ch[end];
start++;
end--;
}
}
}
二、找出字符串中第一个匹配项的下标
一开始的想法;
class Solution {
public int strStr(String haystack, String needle) {
char chars1[] = haystack.toCharArray();
char chars2[] = needle.toCharArray();
if (chars1.length < chars2.length) return -1;
int cnt ;
for (int i = 0; i < chars1.length; i++) {
cnt=0;
while (chars1[i] == chars2[cnt]) {
i++;
cnt++;
if (cnt == chars2.length) return i-cnt;
}
}
return -1;
}
}
但是在这个例子中执行出错:
发现问题在于,当匹配到issi时,p不匹配,于是needle字符串回到一开始的i开始扫描,但是haystack字符串时是继续刚刚的位置missi,从下一个s开始扫描,就出现错误。于是借鉴代码随想录的想法。
KMP算法:
目的:解决字符串的匹配问题。
时间复杂度:n为文本串长度,m为模式串长度,整个KMP算法的时间复杂度是O(n+m)的。
前缀:包含首字母不包含尾字母的字符串
例如:{a,aa,aab,aaba,aabaa}
前缀:包含尾字母不包含首字母的字符串
例如:{f,af,aaf,baaf,abaaf}
最长相等前后缀:看前缀后缀有没有相等元素
例如:{aaba--->1(a),aabaa--->2(aa),aabaaf--->0}
前缀表:每一个位置数字表示:整个字符串当前位置之前的字符串的最长相等前后缀长度。
例如:整个字符串为:aabaaf,那么其对应的前缀表是010120。
为什么需要前缀表,是因为在我们设计字符串匹配的时候,(假如说有一个字符串叫in,我们要在另一个字符串out里面找它)如果In的前面一大部分都和out里面某一部分匹配但是就最后几个字符不匹配,那我们就要在out里面回退,返回上次匹配的开头,从下一个字符再重新匹配in,但是这样很浪费时间,因为我们已经在out里匹配in的一部分字符串了,我们可以回退到某一个位置,匹配之前不匹配的那部分字符串即可。那么这个恰当的位置怎么找呢?
例如这两个字符串,已知 aabaaf对应的前缀表是010120,现在扫描到f(模式串)发现不匹配,这时文本串扫描到b,那么我们找f的前一个字符a所对应前缀表的值2,于是下一次回退到aabaaf中索引为2的位置,即从b(模式串)开始在文本串的位置(b)继续往后匹配。
还有一种匹配方式,是得到 aabaaf对应的前缀表后,将该表右移,前面空位补为-1,比如现在前缀表是010120,那么右移之后就变成-101012,这时扫到f(模式串)发现不匹配,这时文本串扫描到b,那么我们找f所对应前缀表的值2,于是下一次回退到aabaaf中索引为2的位置,即从b(模式串)开始在文本串的位置(b)继续往后匹配。--->next数组
j指向前缀末尾位置,i指向后缀末尾位置。
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++) {//开始扫描haystack
while (j > 0 && needle.charAt(j) != haystack.charAt(i)) //扫到哪处不相同,得到当前前缀表的上一位,将needle索引设置为前缀表上一位的值
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++) {//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; //将当前最大相等前后缀长度记录下来
}
}
}
三、重复的字符串
暴力解法:
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String s1 = in.nextLine();
System.out.println(repeatedSubstringPattern(s1));
}
public static boolean repeatedSubstringPattern(String s) {
int zhizhen1 = 0, zhizhen2;
for (int i = 1; i <= s.length()/2; i++) {
zhizhen2 = zhizhen1 + i;
while(s.charAt(zhizhen1) == s.charAt(zhizhen2)){
if(zhizhen2 == (s.length()-1)){
if(s.length()%i ==0) return true;
//注意这里两个if条件如果写在一起(if1&&2)测试aabaaba就会出现超出索引错误
else return false;
}
zhizhen1++;
zhizhen2++;
}
zhizhen1 =0;
}
return false;
}
}
还可以用之前的KMP算法:
如果某字符串是重复字符串,那么除去其最长相等前后缀得到的字符串就是最小重复字符串,具体推导可以参考;
class Solution{
public boolean repeatedSubstringPattern(String s) {
int n = s.length();
int j =0;
next[0] = 0;
int [] next = new int[s.length()];
for(int i = 1;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;
}
if(next[n-1] > 0 && n%(n-next[n-1]) == 0) return true;
else return false;
}
}
以上来自于代码随想录。