数据结构与算法分析笔记与总结(java实现)--字符串6:两串旋转练习题(*)

本文介绍如何判断两个字符串是否互为旋转词,并详细解析KMP算法在字符串匹配中的应用,包括覆盖函数数组的计算及其如何提升匹配效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:如果对于一个字符串A,将A的前面任意一部分挪到后边去形成的字符串称为A的旋转词。比如A="12345",A的旋转词有"12345","23451","34512","45123"和"51234"。对于两个字符串A和B,请判断A和B是否互为旋转词。给定两个字符串A和B及他们的长度lena,lenb,请返回一个bool值,代表他们是否互为旋转词。测试样例:"cdab",4,"abcd",4  返回:true

思路:这题与前面String2:左旋字符串题目近似。String2中是给出一个字符串,求出左旋k个字符后的字符串。本题是给定两个字符串,判断是否是旋转字符串。求字符串旋转k后的新的字符串可以看做单词反转问题通过2次反转进行解决,也可以使用str=str+str拼接成为大字符串后移动k个字符后进行截取length个字符来实现。本题中判断两个字符串是否是旋转关系,由于k不确定,因此方法1是假设k从0开始到k进行逐一反转再与str2进行比较,每次旋转时间复杂度为O(n),共要旋转n次,故时间复杂度为O(N^2),方法2是拼接成为大字符串后从k=0开始截取length长度的字符串与str2进行比较,要比较n次,每次比较要对length=n长度的字符串进行遍历,因此时间复杂度是O(N^2),空间复杂度为O(n)--如果仅仅是左旋K个字符,2种方法时间复杂度分别为O(n)和O(1),空间复杂度为O(1)和O(n).

本题中首先拼接得到大字符串,然后从k=0~k=length-1进行遍历,每次长度为length的字符串,看是否匹配str2。对于字符串的匹配,可以使用kmp算法,kmp算法专门用于进行字符串的匹配,在长度为n的字符串搜索匹配长度为m的字符串的时间复杂度可以降低为O(m+n).所以本题关键是kmp算法的理解和使用。

字符串的str1.contains(str2)方法的底层实现就是使用kmp算法来进行匹配的。可以使用contains()方法,但是对于kmp算法也要掌握。

基于String类库的代码:

importjava.util.*;

publicclass Rotation {

    public boolean chkRotation(String A, intlena, String B, int lenb) {

        // write code here

       if(lena!=lenb){

           return false;

       }

        String C = A+A;

            //直接使用了String类库的contains()方法,其底层实现时KMP算法

        return C.contains(B);

    }

}

 

基于KMP算法的代码:

Kmp算法的原理容易理解,就是避免每次匹配失败后有从pattern串的头部开始匹配,而是通过借助pattern串的覆盖函数,跳过不可能匹配的位置,直接从可能匹配的位置开始比较。

已知目标串target和模式串pattern:

步骤①求出覆盖函数的数组

对于pattern,先得到这个字符串的覆盖函数数组next[],对于数组next[],其中的元素next[i]是指i前面的字符串中覆盖的字符个数,所谓覆盖是中一个字符串中开头的k个字符和结尾的k个字符相同,例如abcedab的覆盖函数是2,即字符ab产生了覆盖。

通过以下函数来计算一个pattern字符串的覆盖函数数组。

publicint[] 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 < next.length) {

            if (ms[pos - 1] == ms[cn]) {

                next[pos++] = ++cn;

            } else if (cn > 0) {

                cn = next[cn];

            } else {

                next[pos++] = 0;

            }

        }

        return next;

    }

}

步骤②:找出模式串在目标串中的位置

例如对于char[] chars={'a','b','a','a','b','c','a','b','a'};得到的next数组是{-1,0,0,1,1,2,0,1,2}

总的实现代码是:

importjava.util.*;

publicclass Rotation {

    public boolean chkRotation(String a, intlena, String b, int lenb) {

        if (a == null || b == null || lena !=lenb) {

           return false;

        }

        String b2 = b + b;

        return getIndexOf(b2, a) != -1;

    }

 

   // KMP Algorithm

    public 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 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 < next.length) {

            if (ms[pos - 1] == ms[cn]) {

                next[pos++] = ++cn;

            } else if (cn > 0) {

                cn = next[cn];

            } else {

                next[pos++] = 0;

            }

        }

        return next;

    }

}

参考:http://blog.youkuaiyun.com/power721/article/details/6132380

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值