leetcode 796. 旋转字符串
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/rotate-string
题目描述
给定两个字符串, s 和 goal。如果在若干次旋转操作之后,s 能变成 goal ,那么返回 true 。
s 的 旋转操作 就是将 s 最左边的字符移动到最右边。
例如, 若 s = ‘abcde’,在旋转一次之后结果就是’bcdea’ 。
示例 1:
输入: s = “abcde”, goal = “cdeab”
输出: true
示例 2:
输入: s = “abcde”, goal = “abced”
输出: false
提示:
1 <= s.length, goal.length <= 100
s 和 goal 由小写英文字母组成
解法一 java 原生方法
由于每次旋转操作都是将最左侧字符移动到最右侧,因此如果 goal 可由 s 经过多步旋转而来,那么 goal 必然会出现在 s + s 中,即满足 (s + s).contains(goal),同时为了 s 本身过长导致的结果成立,我们需要先确保两字符串长度相等。
代码演示
public boolean rotateString1(String s, String goal) {
if(s.length() != goal.length()){
return false;
}
String str = s + s;
return str.indexOf(goal) == -1 ? false : true;
}
KMP 算法
我们主要想通过这道题,来学习下KMP算法,KMP算法的时间复杂度 和indexOf 是 一致的,
什么是KMP算法:
KMP算法(Knuth-Morris-Pratt算法)是一种用于查找字符串中是否存在某个模式的算法,时间复杂度为O(m+n),其中m是模式串的长度,n是文本串的长度。该算法是由Knuth、Morris和Pratt三人于1977年共同提出的,它利用了模式串的前缀信息来避免无效的匹配,从而提高匹配效率。
KMP算法的核心思想是,当模式串中的某个字符与文本串中的某个字符不匹配时,能够利用模式串的前缀信息快速跳过一定的文本串长度,避免从下一个字符重新开始匹配。具体来说,KMP算法通过构建一个跳转表(也称为失配表或next数组),来记录模式串中每个位置之前的最长公共前后缀长度。
构建跳转表的过程如下:
- 初始化一个长度为m的next数组,其中next[0]=-1,表示模式串的第一个字符之前没有前缀可以跳转。
- 从模式串的第二个字符开始,遍历模式串和next数组:
a. 当模式串的当前字符与文本串的当前字符匹配时,继续向前匹配。
b. 当模式串的当前字符与文本串的当前字符不匹配时,需要找到一个位置进行跳转。这个位置就是next[i-1],即模式串中前一个不匹配的位置。由于next数组记录的是最长公共前后缀长度,因此可以从next[i-1]处跳转到next[i],而不用重新从文本串的下一个字符开始匹配。
c. 在跳转的过程中,需要更新next数组:如果模式串的下一个字符与文本串的当前字符匹配,则将next[i+1]=next[i]+1;否则,从模式串的下一个字符重新开始遍历,即next[i]=0。
- 跳转表构建完成后,就可以利用它来进行匹配操作了。从文本串的第一个字符开始,按照上述方法进行匹配,直到找到一个位置i,满足模式串中从0到i的子串与文本串中从0到i的子串完全匹配,或者无法继续匹配为止。
KMP算法是一种经典的字符串匹配算法,它利用了模式串的前缀信息,避免了无效的匹配,提高了匹配效率。在实际应用中,KMP算法被广泛应用于字符串处理、文本处理、模式识别等领域。
具体算法代码 可以查看KMP–高效字符串匹配算法
代码演示
/**
* 旋转字符串
* @param a
* @param b
* @return
*/
public static boolean rotateString(String a, String b) {
if (a == null || b == null || a.length() != b.length()) {
return false;
}
String b2 = b + b;
return getIndexOf(b2, a) != -1;
}
/**
* KMP 查找子串出现的第一个位置,没有返回-1.
* 类似java的 indexOf();
* @param s
* @param m
* @return
*/
public static int getIndexOf(String s, String m) {
if (s.length() < m.length()){
return -1;
}
char[] ss = s.toCharArray();
char[] ms = m.toCharArray();
int si = 0;
int mi = 0;
int[] next = getNextArray(ms);
while (si < ss.length && mi < ms.length){
if (ss[si] == ms[mi]){
si++;
mi++;
} else if (next[mi] == -1) {
si++;
}else{
mi = next[mi];
}
}
return mi == ms.length ? si - mi : -1;
}
public static int[] getNextArray(char[] ms) {
if (ms.length == 1){
return new int[]{-1};
}
int[] next = new int[ms.length];
next[0] = -1;
next[1] = 0;
int pos = 2;
int cn = 0;
while (pos < ms.length){
if (ms[pos - 1] == ms[cn]){
next[pos++] = ++cn;
} else if (cn > 0) {
cn = next[cn];
}else{
next[pos++] = 0;
}
}
return next;
}