判断两个字符串s1和s2是否互为旋转词,互为旋转字符串

判断两个字符串s1和s2是否互为旋转词,互为旋转字符串?

提示:字符串匹配算法KMP算法,速度极快,其应用题完全适合在面试的时候用来优化!

之前咱们学过KMP算法:o(n)速度匹配两个字符串
还有KMP算法必须要预备的预设数组信息:next数组
(1)KMP算法预备知识:字符串match的每一个位置i之前的字符串,前缀与后缀匹配的最大长度是多少?
(2)KMP算法:在字符串s中搜索匹配查找match字符串,如果能找到返回首个匹配位置i,否则返回-1

这几个文章你必须学会了,学透了
才能搞懂本题怎么优化!


题目

判断两个字符串s1和s2是否互为旋转词,互为旋转字符串/


一、审题

示例:
s1=123456
s2=
234561
345612
456123
561234
612345
s1经过循环平移得到的任意一个字符串s2都是s1的旋转词,旋转字符串


暴力解

自然就是按照定义来
从s1的i=0–N-1位置,每一个位置i,都跟s2从头到尾来对比一遍

如果s1长度N,s2长度M=N,因为旋转词起码长度相同的
那这算法复杂度就是o(N^2)
挺复杂度的,肯定不行

代码自然也不难:

//暴力解判别俩字符串是否互为旋转词,返回Boolean
    public static boolean turnWord1(String str1, String str2){
        if (str1.length() != str2.length()) return false;
        char[] s1 = str1.toCharArray();
        char[] s2 = str2.toCharArray();

        boolean isTrun = false;
        for (int i = 0; i < str1.length(); i++) {
            int k = i;//从0位置开始
            for (int j = 0; j < str2.length(); j++) {
                if (j == 0) isTrun = s1[k] == s2[j];//第一次先给它true,因为我们需要连续多与,不能此次都有false
                else isTrun &= s1[k] == s2[j];//持续与,最后看看都是true那就好
                k = nextIndex(k, s1.length);
                if (k == i) break;//若果回到了当初这个i位置,则退出本次字符串的对比,i到下一个位置
            }
            if (isTrun) break;//如果找到一个旋转词,退出就行
        }
        return isTrun;
    }

    //循环坐标;
    public static int nextIndex(int i, int max){
        return i + 1 == max ? 0 : i + 1;//上限时就返回0,否则i+1
    }

    public static void test(){
        String str1 = "12345";
        String str2 = "45123";
        System.out.println(turnWord1(str1, str2));
    }
true

优化解:KMP算法加速到o(n)

咱 这么处理,让s1尾部再拼一个s1
然后用KMP算法判断s2是否为s1的子串

在这里插入图片描述
这样,通过额外空间复杂度的消耗,用KMP加速就搞定了。

因为旋转词是平移的结果,自然,s1+s1里面就包含了s1的所有旋转词。【这是知识点,见过就学会它】

所以,这个题优化的关键在哪?KMP算法
而KMP算法的关键又在对s2建立next信息数组

(1)KMP算法预备知识:字符串match的每一个位置i之前的字符串,前缀与后缀匹配的最大长度是多少?
(2)KMP算法:在字符串s中搜索匹配查找match字符串,如果能找到返回首个匹配位置i,否则返回-1

这俩非常重要的基础知识点:一定给学透了,本题就迎刃而解!


KMP预设数组信息next(对s2建立的)【手撕代码】

//复习,构建s2的next数组,咱们要反复给它手撕,彻底搞会了
    //next怎么推导?自己画个图,有i-1的next信息,求i好说,对比i-1位置和y=next[y]的字符
    public static int[] writeNextInfo(char[] str){
        int N = str.length;
        int[] next = new int[N];
        next[0] = -1;//next[1]=0默认了
        
        int i = 2;//从2开始
        int y = next[i - 1];//对比之前那个y2
        while (i < N){
            //对比i-1位置和y=next[y]的字符,相等就是y+1
            if (str[i - 1] == str[y]){
                next[i++] = y + 1;
                y++;//y要自动往后挪动,继续比下一个位置,因为i和y2已经对比上了。
            }else if (next[y] > 0) y = next[y];//有可跳转的地方才去
            else next[i++] = 0;//没有位置可以跳转了,自然就是0
        }
        
        return next;
    }

KMP算法,手撕代码

//熟练手撕KMP算法,思想很重要,代码真的超级简单的
    public static int kmpMatchStr12(String s, String match){
        if (s.equals("") || s.length() == 0 || s.length() < match.length()) return -1;
        
        char[] str = s.toCharArray();
        char[] m = match.toCharArray();
        int N = str.length;
        int M = m.length;
        
        //对m构建next
        int[] next = writeNextInfo(m);
        
        //从xy=0开始对比,相等就往后挪,否则看y=next[y]【当然保证y!=-1才能,否则就只能让x++】
        int x = 0;
        int y = 0;
        while (x < N && y < M){
            if (str[x] == m[y]){
                x++;
                y++;
            }else if (next[y] != -1) y = next[y];//舍弃0--j-1那段重复对比
            else x++;//y都回到原点了,对不上就只能让x++
        }
        
        //xy谁越界都行,保证y=M才能匹配上,长度就是y,x-y就是起始位置
        return y == M ? x - y : -1;//否则没有就是-1
    }

破解本题,手撕代码

代码流程:
(1)咱 这么处理,让s1尾部再拼一个s1
(2)然后用KMP算法判断s2是否为s1的子串

非常简单,问题不大:

    //判断s1和s2是否互为旋转词
    public static boolean bothTurnWord(String s1, String s2){
        if ((s1.equals("") && !s2.equals("")) ||
                (!s1.equals("") && s2.equals("")) ||
                s1.length() != s2.length()) return false;//长度都不一样

        //(1)咱 这么处理,让s1尾部再拼一个s1
        s1 += s1;
        //(2)然后用KMP算法判断s2是否为s1的子串

        return kmpMatchStr12(s1, s2) != -1;//不是-1才行哦
    }

    public static void test2(){
        String s1 = "12345";
        String s2 = "45123";
        System.out.println(turnWord2(s1, s2));
        System.out.println(bothTurnWord(s1, s2));
    }

    public static void main(String[] args) {
//        test();
        test2();
    }

结果:

true
true

显然,一定要把KMP算法给熟悉透了,才能轻松搞定本题,本题也就是很简单的事情。


总结

提示:重要经验:

1)KMP算法的预设数组信息next,代表i之前前缀和后缀字符串的匹配长度,也是0–i-1上第一个与i字符不配的位置
2)MMP算法的思想非常非常简单,舍弃思想,那段已经匹配过的前缀串的重复比对,加速到o(n)
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰露可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值