字符串6:两串旋转练习题(*)

题目:如果对于一个字符串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类库的代码:

import java.util.*;
public class Rotation {
    public boolean chkRotation(String A, int lena, 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字符串的覆盖函数数组。

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;
    }
}

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

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

总的实现代码是:
import java.util.*;
public class Rotation {
    public boolean chkRotation(String a, int lena, 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.csdn.net/power721/article/details/6132380


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值