【题解】字符串轮转(一个KMP还不如暴力题目)

83 阅读2分钟

题目

1664461421273.png

难度

力扣难度简单,实际难度简单

分类

字符串匹配KMP

思路

这道题基本的解题思路都是先比较长度,长度不等直接返回,长度相等的情况下 把其中一个字符串翻个倍如String s3 = s2 + s2;

原因是因为既然是旋转而成的那前面的字符原本是被后面的字符顶到前面的,如123456往后推以下就是234561

那么234561如果翻个倍234561234561实际上能把原来的123456包含在里面,那要做的就是做字符串匹配。官方的题解是直接s3.contans(s1);非常直接,而且效率非常高。这里十分不理解,contans方法是暴力解的

我的思路是使用KMP算法需要对s1计算一个next数组,每个位置代表了这个位置之前的字符串中前缀字符串和后缀字符串的最长相同字符串长度(前缀字符串不能包含尾字符,后缀字符串不能包含头字符)。 KMP中定义next[0]为-1,而next[1]也是固定的是0,而next[i]则依赖于s1[next[i-1]]与s1[i-1]是否相等,如果相等则代表前缀字符串和后缀字符串的最长长度可以多一位了,所以next[i] 等于 next[i-1] + 1,如果不相等则等于0;求出next数组之后,用双指针去遍历s1和s3相等则一起检查下一位,不相等则把s1的指针指向next[index]的位置进行回调,然后继续走,但是如果next[index] = -1的话则不能调因为index已经到0了,所以代表着要调s3的指针,从下一个位置重新开始比较。

这个算法的理论时间复杂度是m+n,但是在这道题上耗时比m*n的暴力题解还慢得多

代码

class Solution {
    public boolean isFlipedString(String s1, String s2) {
        if (s1.length() != s2.length()) { return false; }
        if (s1.length() == 0) { return true; }
        if (s1.length() == 1) { return s1.charAt(0) == s2.charAt(0); }
        if (s1.length() == 2) {
        return (s1.charAt(1) == s2.charAt(0) && s1.charAt(1) == s2.charAt(0)) || (s1.charAt(1) == s2.charAt(1) && s1.charAt(0) == s2.charAt(0));
        } 
        String s3 = s2 + s2; 
        Integer[] S1Next = new Integer[s1.length()];
        S1Next[0] = -1; S1Next[1] = 0; for (int i = 2;i<s1.length();i++) { if (s1.charAt(i - 1) == s1.charAt(S1Next[i-1])) { S1Next[i] = S1Next[i - 1] + 1; } else { S1Next[i] = 0; } } int index1 = 0,index3 = 0; 
        for (;index3 < s3.length() && s1.length() - index1 <= s3.length() - index3;)
        { if (s1.charAt(index1) == s3.charAt(index3))
            {
            index1++; 
            index3++;
            if (index1 == s1.length()) { return true; }
            } else if (S1Next[index1] == -1)
            { index3++; }
            else { index1 = S1Next[index1]; } } 
            return false; } }