1. 前言
本文的一些图片, 资料 截取自编程之美
2. 问题描述
3. 问题分析
解法一 : 循环arr.length 次, 每次比较arr中是否包含目标字符串
如果不包含, 则将arr向右移动一位, 继续循环
否则 跳出循环 返回true
解法二 : 如果目标字符串是arr的循环移位子字符串, 那么目标字符串必定是 arr + arr, 的子字符串
当然在进入方法的时候, 需要对目标字符串 和arr的长度进行校验一下 避免一下情况 : 目标字符串为 : “aa”, arr 为 : “a”
4. 代码
/**
* file name : Test03StringShiftContains.java
* created at : 8:48:35 AM May 27, 2015
* created by 970655147
*/
package com.hx.test04;
public class Test03StringShiftContains {
// 判断src循环移位是否存在能够包含dst的子字符串
public static void main(String []args) {
char[] src = new char[]{'a', 'b', 'b', 'c', 'd' };
char[] dst = new char[]{'c', 'd', 'a', 'b' };
stringShiftContains01(src, dst);
stringShiftContains02(src, dst);
}
// 思路 如果src的长度小于dst的长度 则直接返回 因为无论src怎么移动 都不可能包含dst
// 否则 判断src是否包含dst 如果不包含 则src向右移动一位 并再次判断 直到移动了src.length位
public static void stringShiftContains01(char[] src, char[] dst) {
if(src.length < dst.length) {
return ;
}
for(int i=0; i<src.length; i++) {
if(contains(src, dst)) {
Log.log("contains");
}
rightShiftOnece(src);
}
}
// 如果dst能够通过src循环移位得到 那么dst必然是src+src的子序列
// 如果src的长度小于dst的长度 则直接返回 因为无论src怎么移动 都不可能包含dst
// 所以先构造一个char[]srcCopy 其内容为src + src 比如 src为"ab" srcCopy为"abab"
// 然后 在判定srcCopy中是否包含dst
public static void stringShiftContains02(char[] src, char[] dst) {
if(src.length < dst.length) {
return ;
}
char[] srcCopy = new char[src.length * 2];
System.arraycopy(src, 0, srcCopy, 0, src.length);
System.arraycopy(src, 0, srcCopy, src.length, src.length);
if(contains(srcCopy, dst) ) {
Log.log("contains");
}
}
// 判断src中是否包含dst字符
private static boolean contains(char[] src, char[] dst) {
return indexOf(src, dst) > 0;
}
// 获取src中dst的子字符序列的索引
// 先进行边界判定, 在循环找出src中和dst第一个字符相同的索引i, 在继续比较dst后面的子字符串 如果找到匹配的 则返回索引
// 否则 继续从(i+1)开始寻找
// 注 : 代码是从 String.indexOf(char[] src, char[]dst)中拷贝过来的
private static int indexOf(char[] src, char[] dst) {
char[] source = src;
char[] target = dst;
int sourceOffset = 0, sourceCount = src.length;
int targetOffset = 0, targetCount = dst.length;
int fromIndex = 0;
if (fromIndex >= sourceCount) {
return (targetCount == 0 ? sourceCount : -1);
}
if (fromIndex < 0) {
fromIndex = 0;
}
if (targetCount == 0) {
return fromIndex;
}
char first = target[targetOffset];
int max = sourceOffset + (sourceCount - targetCount);
for (int i = sourceOffset + fromIndex; i <= max; i++) {
/* Look for first character. */
if (source[i] != first) {
while (++i <= max && source[i] != first);
}
/* Found first character, now look at the rest of v2 */
if (i <= max) {
int j = i + 1;
int end = j + targetCount - 1;
for (int k = targetOffset + 1; j < end && source[j]
== target[k]; j++, k++);
if (j == end) {
/* Found whole string. */
return i - sourceOffset;
}
}
}
return -1;
}
// 将src中各个字符向右移动一位
private static void rightShiftOnece(char[] src) {
char tmp = src[src.length-1];
for(int i=src.length-1; i>0; i--) {
src[i] = src[i-1];
}
src[0] = tmp;
}
}
5. 运行结果
6. 总结
这个问题, 对于第一种思路来说, 虽然想到非常容易, 但是效率并不高, 因为每一次都需要和dst进行一次contains操作
第二种思路 是比较巧妙的
我个人的想法是, 将原字符串的元素做成一个循环单链表, 最后一个元素.next 接到第一个元素, 这样, 然后在查找子串, 当然和上面的第二种思路 基本上是一致的, 当然 也需要进行长度的校验
注 : 因为作者的水平有限,必然可能出现一些bug, 所以请大家指出!